Whenever I think about versions, SemVer is the first thing that comes to my mind.
SemVer is a clear specification for version numbers, defining version as vX.Y.Z with optional suffix. It explains what the version consists of and when to increment each component. The description is precise and this specification is becoming a de facto standard. It works well for all kinds of libraries and programs.
However, there are some cases where SemVer is not the ideal choice.
When is it hard to use SemVer?
Distributed systems. Let’s take a microservice application as an example. Each microservice fits perfectly into the SemVer use case.
But what should we do with a project that connects all of these components?
When should we increment the major, minor, and patch numbers?
What if there is a new component?
What if one is deleted?
Sure, we can write rules describing all of that, but this kind of ruleset would not be obvious and would essentially be an extension to the standard. It’s not simple, that’s for sure.
Operating systems. It is not true that there are only a few operating systems on the market. In the embedded Linux world, there are plenty of them. In a sense, there are as many operating systems as there are device models on the market. As an embedded developer, I sometimes wonder what version number to put on a new release. Let’s look at mainstream operating systems:
- Debian (and macOS, too) has versions with three numbers. I don’t think that it is an implementation of SemVer, though.
- Ubuntu (and Windows, too) has versions named after the date when they were released. In the case of Ubuntu, we can expect a new release every year, and the release number will match the year.
- Arch Linux. In this case, it is impossible to tell what version of Arch Linux is being run. On the “Download” page, there is a date describing the release number. It is the same with tags of the Arch Linux Docker image.
Hardware is another tricky example. SemVer makes sense to some extent. It’s not like we can’t print “v1.3.84” on the device label or write this string to device memory during production. There may be a problem, if we can only use a few bits of data reserved for the revision number. In that case, the only thing we can do is stay with a single-number revision, which has limited power to describe changes.
Video games are another area where I notice SemVer is beginning to be used. In the past I used to see game versions described with one or two numbers, often accompanied by an additional “build number,” which was often a multi-thousand value.
API versions are part of the API, in a sense. It is common to expose only one number as the API version, with strong compatibility guarantees. Having a single number that is supposed to remain unchanged for a long time is surely very convenient for developers integrating their applications with a given API.
Alternatives to SemVer
I believe that SemVer works for the majority of software projects. It is a widely known standard, so there isn’t much to think about. Only in a few cases would I go for something else.
- When I work with non-software deliverables that don’t change often, I prefer a single number that I increment with every change. This is because it’s hard to match the semantics of each number. I don’t want to wonder if changing the font in a document breaks compatibility, counts as an improvement, or just as a fix.
- When I don’t know exactly what was changed, such as in a third-party component (a Docker image for example). The community sometimes applies the version of the primary software as the image tag.
python:3.13only indicates the Python version, not the underlying OS. However, this approach does not work with intermediate images, such as those used for building software. In those cases, I usually choose the timestamp as a version tag so I don’t have to worry about what the changes mean.
Summary
Versions are important. In environments where we need to constantly keep our dependencies up to date, SemVer provides some level of trust that an upgrade should not break anything. Still, this guarantee does not mean too much, because we always have to verify everything.
You just can’t put a label on some things. And apparently, putting a version on things isn’t always obvious, either.
