An Immutable Version number is a version number with a format
ivNUMBER(-LABEL)
for example: iv2019.01.01-beta.3
iv
identifies the version number as being an Immutable Version number and declares it follows this specificationNUMBER
is a numeric string with any number of optional .
(dot) characters for human readability. Dots are allowed anywhere in the NUMBER
part except first and last character and are considered whitespace - they do not carry information.NUMBER
with dots removed monotonically increases with every new version.LABEL
can be any string of alphanumeric characters, dots or hyphens. Labels contain meta information for humans, such as nature of the release, source package version for language bindings, etc., and are ignored by package managersIn Immutable Versioning
Dependency management is one of the most difficult and common problems of software development based on open source software packages. As systems grow, they depend on more and more packages and it gets harder and harder to keep the dependencies up to date, while staying compatible with their APIs and behaviours.
The cause of incompatibilities is in dependencies breaking their original “contract” as they evolve. The APIs change in a way that removes calls, requires more input, provides less output or changes side-effects. Most versioning schemes address this by assigning meanings to portions of the version number. In Semantic Versioning a change of the first part - the major version - signifies a break in compatibility requiring work to upgrade. The version number becomes an agreed way for the package maintainer to signify the intended effect of the update on compatibility.
Conversely, consumers of the packages can usually declare a compatibility mask, that allows them to lock a portion of the version number to a specific number, allowing automatic updates as long as the changes are small enough.
Choosing this mask is a balancing act between receiving automatic patches for longer and assuming too much forward compatibility, where an upgrade assumed to be safe based on its version number breaks compatibility anyway. The ability to mask versions and keep a portion fixed also implies a promise of long-term maintenance of (at least) all the version lines that are meant to be compatible (“major” versions), but sometimes even more granular than that. This leads to a branching of revision history causing a maintenance nightmare for package maintainers trying to fix issues across all supported version lines.
Immutable versioning proposes a solution to these problems by requiring full backwards compatibility of the public APIs and behaviours (side effects) for the lifetime of the package.
This is a strong requirement but it allows safe upgrades and lets the maintainer focus their efforts solely on the latest version of their package. Any break in compatibility is considered an issue to be fixed.
The version number itself is used as metadata to convey information to humans, namely, the ordering of versions. For two versions it is always decidable which of them is the newer one. Authors can chose a versioning scheme to also convey other information, such as the versions age (implying confidence). A recommendation is to use any subset of a date formatted YYYY.MM.DD.HH.MM.SS
as the version number.
In order to work as a system, Immutable Versioning also puts a limited set of requirements on maintainers and package management software, and sets maintenance expectations for packages.
Packages can still evolve in backwards compatible ways by:
Packages versioned with Immutable Versioning are NOT allowed to:
In order to facilitate safe automatic upgrades, it is necessary for package management systems to allow:
Adopting Immutable Versioning sets the following maintenance expectations on the package:
This allows maintainers to focus their effort on the latest versions of the package and effectively ignore deprecated APIs and old versions.
Immutable Versioning aims to remove breaking changes from open source software as much as possible, but at times they are unavoidable. Breaking changes forced by an upstream dependency (e.g. a service the package depends on being sunset) are out of scope of this specification, as the handling is highly dependent on specific circumstances.
The only guidance we can offer is that forced breaking changes need to be taken seriously (the more seriously the more popular the package is) and handled like any other forced breach of contract: they should be communicated long enough ahead of time for consumers to adapt and alternatives or guidance should be offered.
Package maintainers should also consider depending on other libraries and services very carefuly, if there is no alternative available to them to deliver the same functionality, should the dependency stop working.
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.
ivNUMBER
, where NUMBER
is a string of numeric characters or dots (full stops), MUST NOT begin with a zero and MUST NOT contain two or more consecutive dots.YYYY.MM.DD.HH.MM.SS
as the NUMBER
using the UTC time zone. For example: iv2019.03.29
-
). The label MUST be a string of ASCII alphanumeric characters, numbers, dots or hyphens in any order. The label MUST NOT be an empty string. The label is intended for additional metadata and MUST be ignored by package management software. For example: iv2019.03.29-rc.1
Immutable Versioning is inspired by ”Spec-ulation” by Rich Hickey from Clojure Conj 2016 and authored by Viktor Charypar, Tech Director at Red Badger. The format of this document and the formal specification is based on the semantic versioning specification.
If you wish to leave feedback, open a GitHub issue.
This work is licensed under Creative Commons Attribution 4.0 International (CC BY 4.0) .