Versioning as Communication

It's notoriously hard to communicate with existing users of a software package. When they were a new user, they skimmed (the first sentence of) your README before copying installation instructions. But in the years since, they haven't checked in at all.

While your blog posts and newsletters go unread, every active user eventually reads a version number when upgrading their dependencies. That little string may be the only bit of new information they ever see from you, so it's important to make it count. Your choice of a versioning scheme informs what information users get:

  • SoloVer (or ZenVer) says "something changed" with no further information
  • CalVer, says "something changed today and the last release was this long ago"
  • SemVer says "something changed and this is how compatible the author thinks it is with the previous version"

Having released versioned software for many years, I find SemVer to be the best versioning approach for most projects. It puts the most important information front and center: how compatible a new version is with the one they're using. That's really all users care about during the upgrade process. Optimizing for upgradeability paves their path, ensuring they'll get to enjoy the hard work you've put into your project.

No True SemVer

Critics of SemVer assert that it only really works if authors adhere to it perfectly, which is hard to do. Sometimes authors deviate on purpose, but more often, breaks are accidental. Furthermore, critics say it's impossible to understand all the ways your code is used in the wild and how compatible your changes will be.

Therefore (the argument goes), if users can't trust SemVer to guide their upgrades and have to read changelog entries anyway, authors should use a different versioning scheme that doesn't bring a false sense of security.

Issues Overblown

While SemVer mistakes certainly happen, there have been great strides in understanding what makes a breaking change and static analysis to prevent mishaps. In any case, it's not worth letting perfect be the enemy of good.

Like I said, versioning schemes exists primarily as communication tools. If you don't use SemVer, your users need to read the changelog entry for every single version they're upgrading between. That's a lot of work and most users will either:

  • not read and upgrade anyway, or
  • never upgrade

Neither of which is great! You've presumably improved your code over time and you want your users to reap those benefits.

By using SemVer, you give users a hint as to how closely they'll need to read the changelog; it's a starting place. If a change is technically a bugfix but will break a lot of users, you're free to upshift the release to a major version instead (a move explicitly allowed by the SemVer spec). This flexibility allows you to communicate via your versioning, which is exactly the goal here.

Going Your Own Way

Though I think SemVer is the best choice, that shouldn't stop you if you think a different approach is best for your project. For instance, Typescript does its own thing (which Josh Goldberg explained in more detail). The most important thing is to clearly communicate if and how breaking changes will happen (like Django does).

If you're doing your own thing, this StackExchange answer does a superb job outlining what you'll think through to pick a versioning scheme. Ultimately, it all boils down to communication. Do it early and do it often! Give your users the best chance at success.

Big thanks to Predrag, Evan, and Vicky for their feedback on drafts and for helping me think through this post.

Further Reading

There's been a lot of discussion on versioning across many years. Here are some of the sources I referenced while writing this (some of which is linked above):