Home

Introducing SemVer Prime!

Thu Jun 20 2024

SemVer Prime1 is one of my attempts at fixing SemVer. It's meant to get you to think a bit more about your versioning. If you'd like more freedom, check out Humane SemVer!

SemVer Prime, or Gödel's SemVer

SemVer Prime is a new way of doing SemVer, which puts more SemVers in your SemVer!

It works using prime numbers and a bit of madness; a bit like Gödel's encoding, really. But an interactive demo is worth a thousand words:

CategoryMajorMinorPatch
API222
ABI333
Network555
Global Version2151

Hmm... Yes I see, this version number is made of version numbers!

Exactly! Specifically, the version number is computed through the following math: each dimension dim of our versioning (API, ABI, Networking Protocol...) is assigned a prime number pdim.

Each section of the global semver is then computed with the following formula:

Vglobal = Πdim(pdimVdim)

The beauty of this formula is that it has a few very convenient properties:

But ultimately, the true beauty in SemVer Prime is the fact that your version number can now have dimensions. This means that it can now communicate what is still compatible, instead of just telling the user that two versions have a given level of global compatibility.

This can be even more precious for projects that want to keep orthogonal sub-projects' version synced: just add one dimension per subproject!

But won't the numbers get very big?

They will! :D

Oh, you mean it as a bad thing? Well, that is SemVer Prime's weak point.

More specifically, it may lead to issues with SemVer-based tooling that parses each number as a bounded integer (which is most of them).

According to a bit of googling I've done, you should generally not expect SemVer tooling to play nice with numbers above JS's MAX_SAFE_INTEGER, i.e. 253 - 1. That does give you a bit of wiggle room (even though the global version grows exponentially).

Soo... back to square one?

Not so fast! Notice that "New Dimension" in the interactive tool? Well that's our escape hatch! When you reach an overflow on one of your dimension, you can follow this protocol:

This transition version is SemVer-legal thanks to the fact that versions with differing majors are never expected to be compatible, but are allowed to. This means that seeing the major decrease, while surprising, is compliant thanks to the new number being guaranteed unique.

One can argue that this breaks the "bigger number = more recent" aspect of versioning that's usually expected of a version numbering system, and it's a very fair critique. However, due to the exponential growth of SemVer Prime's values, I tend to think of it as "not something a human should interact with in its 'compressed' form": comparing 10-ish digit numbers is surprisingly hard!

As such, SemVer Prime is mostly designed to allow compressing a set of versions into a single version number that interacts well with SemVer-based tools... Except for the ones that tell you which version is the "latest"2...

Ok! I'm sold! How do I actually use this

You should start by defining your "key": the ordered list of dimensions you'll be keeping track of. You can always add more dimensions later, so you don't need to overthink it too much.

You should sort your key such that versions that are likely to change more often get assigned smaller primes. For example, if your library is a networking protocol, you'll probably want "Network" to be near the end of your key, as this is usually something we avoid making breaking changes to as upgrading requires a full redeployment for users3.

If your project has a set of "official" bindings, you may want to version each of them independently by adding one dimension per supported language familly; placing your higher support tiers at the begining of the list.

You should advertise that you're using this specific way of versioning to your users, ideally giving them an easy way to obtain the multi-dimension equivalent of your current version4.

Conclusion

While it is a lot more complicated than Humane SemVer, SemVer Prime has the quality of allowing users to identify what changes have been enacted on their dependencies purely through version numbering alone. In fact, dividing a new version by the one you currently use (adding 1 to each before doing that) gives you a list of the dimensions that have changed!

I find this property awesome, and will personally endeavour to push projects I have a voice in to transition to either SemVer Prime if they could also benefit from its mutli-dimensionality, or to Humane SemVer if they don't.

And on that note: at the time of writing, stabby's latest release is 5.1.0, which will be its last non-SemVer Prime version, as I would like to start keeping track of its API and ABI separately.

I leave you with a more convenient (and two-way) SemVer Prime translation tool:

Usage (describing each text area from left to right):

1: I swear this one's not yet another streaming service. That would be SemVer Plus...2: This is actually SemVer Prime's biggest issue in my opinion, as this could make it hard for package repositories to accurately show your project's latest version.3: As an aside, certain dimensions, like "Networking", may refer to bidirectional dependencies: network peers both depend on each other using a compatible protocol. In such conditions, any update is either a patch or a major5.4: I'm planning on eventually creating a badge system for SemVer Prime, but I'll first need to figure out a 0-cost way to host that. If you know of such a hosting solution, please let me know :)5: Note that if your networking protocol supports multiple internal versions of your protocol, you're probably better off giving each of these internal versions their own dimension. When a release adds support for that protocol, bump that dimension's minor; and bump that dimension's major when support is removed. If it doesn't, keep your life simple and just bump the major of a single networking dimension everytime you break it.

Comments

Want to leave a comment? Open a PR with your comment in it!