cargo-semver-checks ends 2024 having improved dramatically over the course of the year: 12 new releases featuring 63 new lints, with 1175 merged PRs from 57 authors across the many repos that make the project tick. Let's recap what we learned, the biggest things we shipped, and the facets of the project that made it to the conference and podcast circuits.

The number of lints in cargo-semver-checks at the end of each calendar year. At the end of 2022 there were 30 lints, at the end of 2023 there were 57, and at the end of 2024 there were 120. This shows a trend of doubling each year. Every year so far, cargo-semver-checks has been able to prevent twice as much breakage as before. The growth is exponential!

All this was made possible through the funding provided by the Rust Foundation Fellowship Program, Amazon, Rerun, Accelerant, Astral, Zoo, the generous individuals who support my work via GitHub Sponsors, as well as the Google Summer of Code program where I was a mentor. To everyone who makes what I do possible, thank you 🙏

Before we dive in, a quick reminder of why cargo-semver-checks exists.

Goal: Fearless cargo update

If you're already very familiar with cargo-semver-checks, feel free to skip ahead.

Since day one, the goal of cargo-semver-checks has been fearless cargo update.

Currently, updating dependencies is a game of Rustacean roulette: run cargo update, then check if we got lucky or if everything is now broken.

The breakage is caused by accidental breaking changes in dependencies, which happen frequently despite everyone's best efforts. More than 1-in-6 of Rust's 1000 most popular crates have accidentally shipped breakage — 1-2 broken releases every week, on average! cargo-semver-checks helps maintainers prevent such breakage by catching it automatically during development, before breaking changes are published and break downstream users.

Even though maintainers are our direct users, the positive impact of cargo-semver-checks is felt by everyone in the Rust community.

We've learned to never update unless forced to

Accidental breakage is "lose-lose" — everyone is worse off:

Since updating is scary, Rustaceans have learned to ~never update unless forced to.

This is the most surprising thing I learned in 2024. I heard variations of it over and over when speaking to companies both big and small:

We never update dependencies. We only update if the security team makes us apply a patch, or if we really need some new feature.

Everyone, probably including your company

Yikes! 😱 The shocking part was how consistently this came up. I'd estimate 95% of all conversations included a variation of that answer!

The outcome is worse software for everyone: less secure, more buggy, with fewer features and worse performance.

I'd like us to do better. To that end, here are the most important things we shipped in 2024.

Highlight: Twice as many lints, in 20% less time

cargo-semver-checks continued its pace of doubling its number of lints every year. We added 63 new lints in 2024, ending the year with 120 lints. This is up from 57 lints at the end of 2023 and 30 lints at the end of 2022.

The number of lints in cargo-semver-checks at the end of each calendar year. At the end of 2022 there were 30 lints, at the end of 2023 there were 57, and at the end of 2024 there were 120. This shows a trend of doubling each year.

Many of the new lints required sophisticated new code analysis. cargo-semver-checks can now analyze the contents of Cargo.toml manifest files, allowing it to catch breakage related to package features.

We can now also determine whether a trait can be implemented outside its crate, A trait that cannot be implemented outside the crate that defined it is called a "sealed trait". There are several techniques for sealing traits offering different tradeoffs — check out this explainer to read more. which enables precisely flagging over a dozen kinds of tricky breakage. This is process is full of edge cases, so I'm thrilled that we were able to ship it after many months of work!

Even though we run twice as many lints compared to last year, the total wall-clock runtime has shrunk by 20%. This was the topic of the Spotlight section of the cargo-semver-checks v0.38 release notes — check it out for more info. We made room in the performance budget for new lints by squeezing more performance out of other parts of cargo-semver-checks.

For example, we cut package indexing time in half by building indexes in parallel, using a faster hash function, reducing indirections and hash calls, etc. This was covered in the Spotlight section of the v0.35 release notes. Performance is won a few percent at a time, and I'm grateful to Jalil David Salamé Messina for contributing ideas, enthusiasm, and code to our need for speed.

Highlight: Lint-level configuration

We also shipped the community's #1 most-requested feature: the ability to configure which lints run on which kinds of release, and how their results are treated (error, warning, etc.).

Users can now specify their preferences in package or workspace Cargo.toml files, making cargo-semver-checks more or less strict as needed by their projects.

Max Carr did this work as part of Google Summer of Code, handling UX design, changes to the output format, test suite improvements, and a serious amount of end-to-end refactoring. He did an awesome job, and working with him was a delight!

Highlight: 9 different rustdoc format versions, and nobody noticed

Keeping pace with Rust releases from 1.75 through 1.84-beta, we shipped support for 9 different, mutually incompatible rustdoc JSON format versions. If this is news to you, it means we did a great job!

As you may know, cargo-semver-checks relies on the unstable rustdoc JSON output format to analyze your crate for breakage. We insulate users from needing to know anything about it — you don't have to pin a specific Rust version, nor use nightly Rust! Just update cargo-semver-checks when you update your project's Rust versions, and everything just works. Magic! ✨ Making this work was a feat of engineering, and is described in more detail in the Spotlight section of the v0.36 release notes as well as in my FOSDEM and RustConf talks covered below.

Seamlessly supporting multiple rustdoc formats lets the format evolve without breaking our users' workflows. We work closely with the rustdoc team on this!

For example, the new format that shipped in Rust 1.83 produces 8% smaller JSON files than before, which in turn cuts 12% — nearly one full second — from the runtime of our users' largest workloads. Who doesn't like free performance upgrades just from updating Rust and cargo-semver-checks?


All in all, it's been a good and very busy year! If your company or your projects benefit from my work, please consider funding it in whatever capacity you can — even small numbers add up! Thank you 🙏 Next week, I'll publish a post describing what that funding will enable in the future.

As a coda, let's revisit this year's media appearances of cargo-semver-checks and its sibling projects.

Conference talks

This year I gave conference talks at RustConf, EuroRust, UA Rust, and FOSDEM, as well as doing a meetup talk at Rust Boston as a practice run of my FOSDEM talk.

My talks at FOSDEM and RustConf covered cargo-semver-checks and SemVer in Rust. My FOSDEM talk, "SemVer in Rust: Tooling, Breakage, and Edge Cases" tackled the common falsehoods programmers believe about SemVer in Rust, from "breaking changes are obvious and easy to catch" to "code I didn't touch cannot be broken."

At RustConf, my talk "Putting an End to Accidental SemVer-Breaking Changes" argued that SemVer breakage costs the Rust community tens of millions of US dollars per year, and showed how cargo-semver-checks can support hundreds of lints over a range of Rust versions and rustdoc format versions without drowning us in maintenance work.

My EuroRust talk, "Build Bigger in Less Time: Code Testing Beyond the Basics" used examples from cargo-semver-checks and the Trustfall query engine to demonstrate useful and underrated testing techniques: invariant testing, snapshot testing, and deterministic simulation. Without those techniques, neither cargo-semver-checks nor Trustfall would have been possible!

At UA Rust, my "Trustfall: A Foundation For Next-Gen Developer Tools" talk showed how building modern dev tools is usually a database problem in disguise. The conference hasn't made the recordings public yet. This was a fun talk, so I hope the video gets published soon! To riff on Greenspun's tenth rule, To save you a click, Greenspun's rule says: "Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp." any sufficiently complex dev tool contains an ad hoc, informally-specified, difficult-to-maintain implementation of half a database. How else would they implement all the intricate rules, handle all the complex data formats, and have acceptable performance even for large projects? Given that's where all dev tools eventually end up, it's best to start by using a query engine like Trustfall from day 1 instead.

I plan to publish each of these talks in blog post form in the coming months, so consider subscribing if you don't want to miss them.

Podcasts and livestreams

In 2024, I had 3 podcast appearances and was a guest on one livestream. I had a blast on all of them, and I occasionally mentioned things I haven't "officially" launched yet, so make sure to check them out 😁

At the beginning of the year, I made the case that Rust needs cargo-semver-checks on Rustacean Station.

In June, I teamed up with Chris Krycho for a cross-language look at semantic versioning on Changelog. Even though SemVer can be quite frustrating at times, Chris and I were in agreement that it's better to fix it with better tooling (such as cargo-semver-checks) than to abandon it in favor of other versioning schemes.

At the end of the summer, I joined the hosts of devtools.fm for a chat on how Trustfall makes cargo-semver-checks possible. We chatted about how everything is a query, how optimizations work, and why Trustfall can change the landscape of how dev tools are built.

Most recently, I joined a livestream hosted by Orhun Parmaksız where we teamed up to write lints for cargo-semver-checks. Anyone can write a lint with a bit of determination, and this livestream is proof of it!

I'm looking forward to what 2025 has in store! Expect more posts describing some of the ideas I plan to work on in the near future 👀

If you liked this essay, consider subscribing to my blog or following me on Mastodon, Bluesky, or Twitter/X. You can also fund my writing and work on cargo-semver-checks via GitHub Sponsors, for which I'd be most grateful ❤

Discuss on r/rust.