Photo by Giammarco on Unsplash

Versioning Systems

Posted: 24 Sep 2021. Last modified on 04-Jun-22.

This article will take about 3 minutes to read.


All software engineering projects require a versioning strategy. Versioning allows us to reason about the relative stability of a build, and create code that depends on published libraries. However, as with most important engineering topics, there are multiple ways of achieving this - all with their own unique pros and cons.

Semantic versioning

The first type of versioning is the most popular for actual releases. There is a host of documentation describing this system in greater detail, but I will try to give a brief overview here.

Semantic versioning prioritizes compatibility. It aims to make consumers of an API aware of the extent of the changes, and give them a heuristic for how risky the changes that are included will be for the consumer’s product. It is split into 3 parts:

which stand for:

Therefore, consumers of an API know pretty much what to expect when someone is upgrading from 1.0.0 to 1.0.1 - not much will change, and you should upgrade as soon as possible to fix the associated bugs. Likewise, if someone is upgrading from 1.5.0 to 2.0.0, they should expect a bit more of a bumpy road while migrating - they should be prepared to rewrite certain parts of their app.

Date-based versioning

This form of versioning is more popular for rolling releases, or ones that are supposed to be out by a certain date. They allow us to reference “snapshots”, which make it easier for testers to keep track of which bugs are still around in the most recent version of the app.

It’s quite common for this versioning system to be used in conjunction with semantic versioning. Typically, an app will have multiple “release channels” that are sent to different users. While a Production channel would usually be using semantic versioning, it is common to have an Alpha channel that only gets sent to internal QA members. This Alpha channel might see a new version published once a week, or even more frequently, at which point QA members will be able to generate a list of bugs to be fixed by the next snapshot.

My favorite example of this kind of versioning would be Mojang’s Minecraft snapshots. They take the current year and week number into consideration for their snapshots, so you’ll be able to keep track of exactly when each snapshot was generated. On a longer cadence, they do semantic releases as well. At the time of writing, their team has released 21W38A and version 1.17.1, meaning that we are in the 17th release of minecraft 1, and the current state of development is the snapshot taken on the year of 2021, week 38, first release.

Incremental versioning

The most ad hoc form of versioning is incremental. This one is the easiest to understand because there is not much to it - every time there is a change, you increment the verion number. This is mostly used for projects which are developed in bursts, without much long term structure. The most common way to represent this is within a semantic versioning format, by starting with either 0 or 1, and ony incrementing the feature version. Semantic versioning is generally easier to integrate into other package management systems, such as NPM. For example, a project might increment from 0.1.0 to 0.2.0 to 0.3.0.

This leaves the door open for them to switch to a more strict form of semantic versioning later - all that it would take is a major version upgrade, to signify that there is going to be a major shift in their process.

Conclusion

At the end of the day, the versioning strategy that you choose is up to you! I would recommend using semantic versioning for production releases, and date based versioning for internal releases.