1. version numbers sort numerically and lexicographically in a sensible way, including across projects and packages which use the same format
2. users get educated that these preciously-held ideas they have about software version numbers are complete superstition. Like "something with a zero major number means not production ready", "something with a zero minor number means I should wait until there's a patch", "something with a major number increase means backwards-incompatible", "something with a minor number increase means backwards-compatible"
3. You know when a particular version (of everything) came out. "We started seeing a wierd bug on X date" no longer is impossible to figure out.
(These are self-contained projects. I suppose semver does make some sense for libraries that you link with.)
Professionally, it's been 99% Perforce for about 15 years, so it's routine to use the submitted changelist number, submitted changelists being numbered in the order they were subsequently committed. Sadly not fixed-width, but at least Explorer sorts them sensibly.
Two difficulties I have had doing this with git:
- there doesn't seem to be a way to get git to enforce UTC, so the dates are in my local time zone (for my projects this is not really an issue, and my timezone is almost UTC anyway)
- the CI system runs separate builds for different targets, and using the git commit timestamp ensures all builds get the same time stamp. But it's then possible to end up with timestamps significantly different from the actual release time, or (worse) out of order. I could probably do something better about this than my current "solution" of doing nothing, but this has only happened a couple of times
1) major public API change. If you don't have a public API, this should never be anything but 1 and can be hidden from the user. End users don't want these they scare them.
2) minor: any planned release that doesn't break API. End users love these and plan around them.
3) revision: unplanned emergency hotfixes. Naming this way means the "next minor" we were talking about with all stakeholders is still the next minor. It also means our version numbers look like our git dag, since this one would be a branch from the last tag instead of main.
4) release: sometimes something goes wrong during the release itself. The first 3 numbers are public, this is internal-only and only appears in git tags and internal deployment notes. This way every push to prod has a unique version number, but all our change management documents are still accurate even if we had to push 2 or 3 times for a single release.
Why? First digit is about compatibility. All the other digits are about planning.
"We're working towards 1.3"
"we think this feature will be in 1.4".
"We had to release 1.2.1 because of an emergency somebody put Arabic text in their profile picture filename and that brought down the site."
"Turns out that trick with the release pipeline didn't work in prod so we had to make 1.2.1.1 while deploying".
I think there needs to be better project definitions around what constitutes a major change.
Projects need to be able to define things like dropping support for old versions of the underlying language in minor versions. So that the last version of support that some people might get is "3.2" and "3.3" may not install at all for them. That means that technically they are in a state where they need to do work to upgrade and are "broken" in a sense, but the actual public API of the software has not changed between "3.2" and "3.3". Supported O/S distro versions should also be able to be abandoned in minor releases. Toolchain updates can also happen in minor releases. Pulling in major versions of dependencies which are technically breaking for anyone who hits a diamond dependency issue, but which produce no major breaking API changes should be able to happen in minor versions.
That means that the contract isn't "I can pull in minor versions and you can never force me to do work" but more strictly that the public API the software exposes won't update.
There's also the problem with semver pinning that projects do where they put hard floor and ceiling pins on all their dependencies, even though their software may be fine with a 5-year old version of the dep (they've just never tested) and it may work fine with the next major release of the dep without any changes at all. Ideally for that last problem, the compatibility matrix fed into the dependency solver should really be a bit more malleable, so that the engineer can realize that the next version of dependency breaks everything and they can retconn the compatibility of their software to pin to the last working version of that dependency. This breaks the perfect immutability of literally everything about a software release, but allows for not being able to predict the future.
Nearly every org I’ve worked in has used semver internally and nearly every time their version numbers were just incremented arbitrarily because there wasn’t an exposed API.
This lead to countless problems, not least of all because semver usually requires one to manually set the version number based on the change log and people are generally pretty bad at changing point releases.
So I’ve usually ended up changing the versioning scheme to build number (generated by the CI/CD tooling) plus some extra information like git hash and/or timestamp - depending on the application and whether that build information can be easily encoded as additional metadata or not.
For applications, it goes just like you said.
For APIs on the network, the caller should only get to control the breaking version their request gets routed to (the rest is abstractly owned by the service provider).
While it does make more sense for them, that it not even clear as it seems. Library authors rarely define the public API because it is very tedious and hard to complete---there are only some sort of fuzzy and implicit "common sense" definitions. Whenever "breaking" changes happen the definition gets stronger (but still incomplete), and over the time it would encompass every observable aspect, as the Hyrum's Law suggests.
In either case semantic versioning is not tremendously useful because either users have an incomplete expectation of what major, minor and patch versions mean, or they will be notified of every possible change and the version distinction becomes useless. Semantic versioning is still useful because it was a codification of existing practice where the expectation can be good enough to avoid most issues. There is no actual value added by the codification in my opinion.
I think more software follows semantic versioning than before it was codified.
Versioning includes many practices outside semantic versioning obviously. Rational numbers. Odd minor version is unstable. Last number at least 90 is unstable. No patch versions. No minor versions. Incompatible minor versions. Dates.
I rejected your claim library authors rarely define the public API. And Hyrum's law makes semantic versioning imperfect but not useless.
The Semantic Versioning codification does attach specific semantics to major, minor and patch versions, but those semantics can't exist without the public API. So the codification itself is nothing more than a stipulation that there should be three major categories of changes. The codification would have been much more useful if it included specific rules for particular languages and contexts, and there are indeed some noble attempts. But they are hardly complete and users can't rely on that. For example Rust Cargo defines a relatively complete semver guideline [1], but it doesn't define whether changing the minimum version of Rust required (MSRV) is a breaking change or not. So a minor version upgrade may or may not increase MSRV and users have to consult the release note to find it out anyway. If users can't be entirely sure if the minor version is breaking or not for their purposes, is the entire stipulation worthy?
[1] https://doc.rust-lang.org/cargo/reference/semver.html
> I rejected your claim library authors rarely define the public API.
I agree that they do think they define the public API, sometimes only in their minds, sometimes in a form of the public source code. But the source code is only a portion of the public API, and any major libraries have suffered from numerous mismatches between authors' and users' definitions of the public API. The public API in semantic versioning clearly means the latter, so my claim that library authors rarely define the public API (of the second sense) still holds. But I should have been more clear about two different notions of the public API---sorry for that.
> Hyrum's law makes semantic versioning imperfect but not useless.
The general idea, yes (you can still communicate that changes have happened). The codification, no (three-part versions are no longer useful).
Generations of programmers come up writing web services where the router forms a hard API layer and they don't see the point in public/private/protected any more, because nobody is ever linking against their code so it doesn't matter.
Version numbers just denote a change happened and you want them to roughly resemble some sort of chronological ordering. Everything else is gasoline for flame wars and company policies.