SemVer might not be right for you
Methodical release of software is a crucial part of the development lifecycle. After the hard work producing the software it would be a shame to put no love and no mind into what the release is named. Names matter.
Each release should have an identified: version number, name, etc. The logic behind each releases identifier should have a consistent and predetermined set of principles otherwise it’s recipe to confuse developers using the software. These principles are termed versioning scheme. A common version scheme is Semantic Versioning (SemVer), and another is Calendar Versioning (CalVer).
Version schemes allows users to distinguish between releases. If the version scheme picked is well chosen the version scheme can even expose valuable information about the contents of the release.
Versioning schemes add value because the version name exposes information to the developer. In this blog post you will learn about two of the most popular versioning techniques: Semantic Versioning and Calendar Versioning and understand when using them can add value to your software project and even how misusing them can take away value from your software project.
The most popular versioning method in the software industry is probably SemVer. Quite fittingly SemVer itself uses SemVer and is current at version 2.0.0.
Three elements (separated by a “.”) make up a SemVer version number: MAJOR.MINOR.PATCH.
According to the standard, the MAJOR number should only be increased when there are changes incompatible with previous releases. This might be function names changing, code deleted, function call signature changed.
When new functionality is introduced in a backwards compatible way the MINOR number is increased.
When backwards compatible bug fixes are made the PATCH number is increased.
The MAJOR.MINOR.PATCH structure also supports additions like pre-release and build numbers. Valid SemVer:
Semantic Versioning overcomes several issues
Exposing the backwards-compatible nature of the release simplifies explaining the impact of using the new release. Once developers know the if the change is backwards compatible, flexible dependency specifications can be used: allow pip to install any release that is 1.x.x because as long as the major number does not change then the upgrade will not break the codebase consuming it. This allows developers to get security fixes and new features with less overhead compared with trawling through diffs to understand if the upgrade will break their codebase.
For a straightforward illustration of how Semantic Versioning may put an end to Dependency Hell think of the library “Firetruck.” It needs the “Ladder” Semantically Versioned package. The ladder was at version 3.1.0 at the time Firetruck was made. You may confidently set the Ladder dependence to be more than or equal to 3.1.0 but less than 4.0.0 since Firetruck uses some functionality initially implemented in 3.1.0.
Now, you may distribute Ladder versions 3.1.1 and 3.2.0 to your pip knowing they will work with any current dependent applications.
Naturally, as a responsible developer, you’ll want to ensure that any package upgrades work as promised. We can do nothing to change the natural world’s chaotic nature except to be attentive.
By using Semantic Versioning, you can save time and effort, which gives you a sensible approach to updating and distributing packages without needing to roll out new versions of dependent programs.
Many teams practice something like SemVer, but there is a big difference between SemVer-ish and SemVer. In fact a project that looks like it uses SemVer but have poor SemVer hygiene can cause developers undue confidence in their ability to suavely upgrade a library. “Oh only the minor version changed. I can upgrade!” resulting in the maintainer practiced poor SemVer hygiene and introducing a backwards incompatible change. The developer that performed the upgrade is going to have a bad time.
Additionally there are complications around what is a backwards incompatible change. If there is a change to a function that the package maintainer considers “private” then is this a breaking change? Technically no, but bad SemVer hygiene from the developers consuming the package might see their codebase break because they are using features that are not part of the public API of the package.
Therefore version numbers are worthless for dependency management without the package maintainer conforming to the formal specification, and the package user adhering to the library’s documented public API.
Backwards compatibility is a very computer-centric concept. What is backwards compatibility when the codebase renders a form in the browser? If the button is moved or a field is removed is that a breaking change? To some users every single UI update would be a breaking change. This problem compounds when we consider accessibility: changing the colour of text could be a breaking change for colour blind people. Changing text to content that use longer words could be a breaking change for dyslexic people. When the codebase being versioned is a website SemVer is probably not the best option to use. It would be SemVer-ish.
When the codebase exposes API endpoints then perhaps the API should be versioned rather than using SemVer-ish numbering for the codebase itself. SemVer release versions are aimed at a particular audience to communicate compatibility. For the API endpoints the audience is the developers that write code that communicates with the API endpoints. They’re not installing the code that exposes API endpoints, they are communicating with the API endpoints therefore it’s the API that should be versioned rather than using SemVer-ish versioning for the codebase.
Another well-liked versioning strategy that has grown in popularity is CalVer, or calendar versioning. CalVer decides to use numbers that correspond to the calendar information issued with each edition, such as 2020–05, 2020.05, and 20.05, rather than utilizing random numbers (all indicating the release time of May 2020)
CalVer offers standard nomenclature for developers:
- YYYY- Represented as a whole year, for instance, 2006,2016,2022.
- YY- Represented as Short Year, for instance, 6,16,22
- 0Y- Represented as 0 followed by the year, for instance, 06,16,22.
- MM- Represented as Short Month, for instance, 2,6,12.
- 0M- Represented as 0 followed by the month, for instance, 02,06,12.
- WW — Short week (1, 2, 33, 52) since the beginning of the year
- 0W- Zero-padded week — 01, 02, 33, 52
- DD- Short day — 1, 2… 30, 31
- 0D-Day zero padding: 01, 02, 30, 31
Note that date segments are 1-based, whereas traditional, incremented version numbers are 0-based, and that the short, zero-padded years are related to 2000. Also, weeks and months/days are typically mutually exclusive.
Both the Gregorian calendar and the custom of UTC are presumptive. Technically, projects can use any calendar as long as they specify which one.
When to use SemVer or CalVer
Context matters, but to over simplify: SemVer is better for versioning libraries, whereas CalVer is better for versioning systems (like a codebase for a website that uses various libraries).
- Do you have a project with a broad or evolving scope? Use CalVer.
- Large systems and frameworks, such as Twisted and Ubuntu. Use CalVer.
- Utility sets that are amorphous, like Boltons. Use CalVer.
- Is your project in any way time-sensitive? Do new project releases depend on modifications made elsewhere? Use CalVer.
- Business needs, including Ubuntu’s emphasis on service schedules. Use CalVer.
- changes to security, such as the requirement for certifying to update certificates. Use CalVer.
- Political alterations, like how pytz handles time zone changes. Use CalVer.
- Library that other cdebases consume? Use SemVer.
Improve your code
Code Review Doctor is a code scanning tool that suggests Python and Django fixes right inside your pull request.