Simple Versioning

Summary

Given a version number NUMBER, increment the:

  1. NUMBER version when you make backward-compatible API changes.

Breaking changes result in a new package with new versioning.

Introduction

From the perspective of dependency management, a software package is solely defined by its API — typically in prose, sometimes backed by a type system. Dependency management cares about only one notion regarding such APIs: given an explicitly specified package A, can some package B — found through dependency resolution — be substituted for it? The answer is “yes” if and only if the API of B is compatible with that of A (where compatibility usually goes beyond what can be checked mechanically in a type system).

As a package evolves, the maintainers release a sequence of successively compatible versions. We propose to assign successive natural numbers to these releases, and call this system “Simple Versioning.”

What happens when a package needs a breaking change? You create a completely new package with independent versioning. For a dependency management system, React version 2.0.0 and React version 3.0.0 relate to each other in the exact same way as React version 2.0.0 and jQuery 2.0.0. To humans, there is a meaningful difference between these two pairs, but this difference has nothing to do with dependency management. We clearly should not complicate computer systems based on irrational human needs.

Simple Versioning Specification (SimVer)

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.

Everything is normative unless indicated otherwise, duh. This is a specification.

  1. There is no need to define software package or version in a specification for versioning software packages; everybody already knows the one, universally agreed-upon meaning of these terms.

  2. Software packages using Simple Versioning must declare a public API. How to do so is out of scope of this specification. Good luck.

  3. Associated with every release of a package must be exactly one version Number, where Number is a natural number (possibly zero).

  4. All package releases must happen in a linear order; the version numbers must correspond to that linear order.

  5. For any two versions Number and Number + 1 of the same package, any dependent on version Number must be able to substitute version Number + 1 without changing... something? Clearly there will be observable changes on some level, since the versions are not equal. The exact notion of which changes are acceptable is out of scope of this specification, and should be communicated by the package authors. In advance, please.

  6. For human reading or machine processing, encode a version number Number as an ASCII decimal number without leading zeros.

Backus–Naur Form Grammar for Valid SimVer Versions

<valid simver> ::= "0" | <positive digit> | <positive digit> <digits>

<digits> ::= <digit> | <digit> <digits>

<digit> ::= "0" | <positive digit>

<positive digit> ::= "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"

Why Use Simple Versioning?

This is not a new or revolutionary idea. In fact, you probably do something close to this already. The problem is that “close” isn’t good enough. Without compliance to some sort of formal specification, version numbers are essentially useless for dependency management. By giving a name and clear definition to the above ideas, it becomes easy to communicate your intentions to the users of your software. Once these intentions are clear, flexible (but not too flexible) dependency specifications can finally be made.

A simple example will demonstrate how Simple Versioning can make dependency hell a thing of the past. Consider a library called “Firetruck.” It requires a Simply Versioned package named “Ladder.” At the time that Firetruck is created, Ladder is at version 3. Since Firetruck uses some functionality that was first introduced in 3, you can safely specify the Ladder dependency as greater than or equal to 3. Now, when Ladder version 4 and 5 become available, you can release them to your package management system and know that they will be compatible with existing dependent software.

As a responsible developer you will, of course, want to verify that any package upgrades function as advertised. The real world is a messy place; there’s nothing we can do about that but be vigilant. What you can do is let Simple Versioning provide you with a sane way to release and upgrade packages without having to roll new versions of dependent packages, saving you time and hassle.

If all of this sounds desirable, all you need to do to start using Simple Versioning is to declare that you are doing so and then follow the rules. Link to this website from your README so others know the rules and can benefit from them.

FAQ

If breaking changes result in new packages, do I need to come up with a new name every time?

Yes. Thankfully, we have come up with a simple and deterministic algorithm: If you need to do breaking changes to a package called Flibble, call the new package Flibble 2, the one after that Flibble 3, and so on. This works great with existing package managers, and humans will just call the thing Flibble most of the time (while automatically becoming more precise when needed).

What about discoverability?

You do not find out about breaking changes from your package manager, but through other channels such as a website or a code repository. Those same channels can also announce switches to completely new packages due to breaking changes. Clearly, there are absolutely no problems whatsoever with Simple Versioning.

What about impersonation? Any troll could publish React 3 after the poor, hard-working React team poured its soul into React 2.

This is only a problem with package managers that provide a single, global namespace. Which is a pretty bad idea in the first place, because it makes meaningful package names a scarce resource (have you ever had the pleasure of trying to name a javascript project before npm had orgs?)

With package-manager-level scopes, this issue vanishes. In fact, you could create a single scope per project, and turn that scope into a discovery mechanism for breaking changes. But wait, wouldn’t that introduce name scarcity? Anyway, there are clearly absolutely no problems whatsoever with Simple Versioning.

Without a distinction between preview releases, bug fix releases, and feature releases, how will I select the correct policy for making all my users automatically and blindly download arbitrary code that any future maintainer of any of my (transitive) dependencies might ever publish?

The same way you should without Simple Versioning.

How can I use Simple Versioning in a world full of Semantic Versioning infrastructure?

Semantic Versioning contains a subset which is isomorphic to Simple Versioning. To embed a Simple Version Number in a Semantic Versioning system, use the semantic version 0.Number.0. This will work with all Semantic Versioning software, and the software will be none the wiser!

Is there a suggested regular expression (RegEx) to check a SimVer string?

0|[1-9]\d*

Why did you make me read a 214-character BNF grammar when there is an equivalent 10-character RegEx?

Because a “specification” without a BNF grammar is too informal to be called a specification.

Sequential version numbering makes concurrent package updates by independent maintainers impossible, because they would assign the same version number to different programs. This precludes truly decentralised, eventually-consistent, delay-tolerant software publishing, thus implicitly entrenching a status quo that assumes complete sovereignty over software packages by a single, privileged author (or group of authors who together act as a single logical author). How dare you?

Well, nobody else seems to care about this either.

But, since you did just ask, I will lay out some ideas. Instead of using successive numbers as version identifiers, you could identify each version by a secure hash. This hash should be both over the data of the version in question and over the version identifier (i.e., the hash) of the previous version, if any. This results in a linked list of hashes. (If you absolutely insist, you can also hash in some data to distinguish, for example, between patch releases and feature releases.)

More accurately, this scheme results in a tree of hashes: different versions of a package can branch off at the same base version. This is great, because it enables concurrent creation of new versions without requiring a consensus mechanism between all package authors for naming them.

A typical follow-up question to this kind of design is how to merge together concurrently published versions which both have features to keep. There is an answer which is as obviously correct and simple as it is boring: add the combined features on one of the concurrent branches, and abandon the other one. Nothing in this system benefits from complicating the underlying data structures and cryptography by adding merge operators.

A tree of versions results in a new challenge: given two versions, how do you efficiently determine whether one of them is a predecessor of the other? Systems such as Reed allow querying the happened-before relation on a tree in logarithmic time, while adding only a constant amount of additional data to incorporate into each version hash. In a peer-to-peer setting where participants might not want to store full version histories, peers can perform these comparisons while storing only logarithmically-sized subsets of the version histories.

Such a system of tree-shaped version histories ultimately erodes the notion of coherent package identities: anyone can fork off of any published version and create their own compatible derivatives. Those might be malicious, or shoddy, but this is not problematic, since there can be no automatic fetching and installation of the newest compatible version anyway (because there is no single newest compatible version).

You can of course establish channels where trusted maintainers can publish their “official” updates, thus restoring the notion of package identities (and linear version histories). In high-trust environments, you could even subscribe to such channels and automatically resolve to the most recent version. To keep things decentralised, delay-tolerant, and eventually consistent, I would recommend using signed bindings as the mechanism for assigning a single name to a continuously updating package identity.

This would be a design for a package manager that is actually decentralised, not one that simply relies on centralised package authorship and places the data on content-addressed peer-to-peer storage. But, you know, this is just an FAQ entry inside a specification of questionable quality, so probably not worth taking seriously.

About

The Simple Versioning specification was originally authored by Aljoscha Meyer, a good-for-nothing who didn’t co-found anything.

If you’d like to leave feedback, feel free to reach out somehow. If that is too much effort, instead hug a person who loves you. Or plant a tree, or something.

Yes, I am aware of simver.org. I am even aware of the same author proposing integral versioning as well. What is more shocking — the fact that these dazzling ideas (and naming choices) should be rediscovered independently, the fact that I deliberately withheld this prior work for dramatic effect, or the fact that Snape kills Dumbledore?

License

Creative Commons ― CC BY 4.0

This page was adapted from the Semver 2.0.0 page, released under Creative Commons ― CC BY 3.0.