SemVer Patch Number
Discussing (and defining) versioning is never simple. It’s a complex issue, not least because of the large number of developers who have been involved in creating solutions for a wide array of environments.
Update package managers have often been created to address the needs of specific systems – Linux (APT and RPM), Python, Maven, Ruby, NPM, Nuget, and so on. It becomes confusing, time consuming, and costly for developers who are collaborating with one another but working on different platforms, as they must adapt to the versioning constraints of each package.
Sequence-based identification arose as a means of addressing this multiplicity of versioning activities, and semantic versioning (SemVer) is currently the most successful of these schemes. In its current iteration, Semantic Versioning 2.0 is helping the software industry to focus on how versioning should be done. SemVer establishes strong, yet streamlined constraints through the use of a three number versioning system (Major.Minor.Patch). It’s not a perfect solution, particularly as it’s proven itself to be flexible enough for people to create their own SemVer adaptations.
Also, although the rules demand that only those three numbers can be used, one can add a “prerelease tag.” For example, one can add information in the development of a Major.Minor.Patch before it is released to the world. We might have Version 2.1.2-milestone61, where “milestone61” is communicating a step in the process of creating a specific semantic version. However, it is forbidden to have a micro-patch on a patch. If we want to execute a patch on v2.1.2, the patch must be v2.1.3.
These strong constraints force those things that are labelled as patches to be real patches. If a patch number adds too many changes, it should raise the concern that if a specific issue on v2.1.2 needs to be fixed – and there is already a v2.1.3 and a v2.1.4, with a quite large amount of code inside those versions – then there will be no way to release a micro-patch for v2.1.2. The contract must be adhered to. To fix the issue on the v2.1 stream, it must be addressed in the appropriate patch sequence and will be labelled as v2.1.5.
It’s a best practice to have strong versioning constraints. It makes the lives of all developers easier, as there’s a whole lot less to decipher. Nevertheless, many companies and many teams have a hard time staying within these strict confines. It’s not rare for developers to use prerelease tags in post-release situations, or to issue micro-patches on top of micro-patches. As a result, we still see very low levels of trust in updates of patch versions.
This is overcome when liquid software updates are flowing, and systems are being updated automatically with the latest version of a given library or piece of software. If a developer has issued the v2.1 stream of their software or library, then every time a patch is ready for release, all users who need and are capable of accepting the upgrade will automatically receive it. And patches will only be released once they’ve been fully tested and verified as proper patches that are fixing bugs, security issues, the behavior of a piece of code, and the like. They cannot contain any database, schema, or strong API changes, nor can they impact the way a system is actually responding. Proper patches should always be capable of being added into running systems, at any time, with zero downtime.
The First Deadly Sin
So the First Deadly Sin of Versioning is committed when one is doing something that looks like SemVer, but in reality is adding a lot of features, a lot of code, and actually changing the behavior of a given piece of software on a patch release. If used precisely as intended, SemVer has the basic requirements to deliver a continuous flow of automatic updates, and we’re certainly seeing more and more evidence of this in the software industry’s execution of continuous integration and continuous deployment.
To keep one’s software soul pure, one must be certain that patches are really patches. And one way to truly know that a patch is a patch is to establish an environment in which such releases are run-of-the-mill. A patch is a fix, it’s an adjustment. It doesn’t require any publicity or ballyhooing because it’s not adding anything. Rather than altering or enhancing what a piece of software is supposed to do, it’s merely assuring that the software continues to do it. Liquid software assumes that the continuous delivery of patches will always be part of the normal flow of any continuous update system. And most importantly, patches should be done by machines. Maintaining consistency in the standardizations that SemVer imposes can make the work of machines quite simple when carrying out comparisons and analyses between versions, and generating sensible versioning of updates.
Upcoming posts in our Deadly Sins of Versioning series will further explore the uses and misuses of SemVer, hash versioning, multiple packages for the same version, and versioning in the code.