Version numbers have existed for a long time, and you have probably seen things like
1.1 or perhaps even more complex forms like
v2.44.3-alpha. At it's core, a version number is simply a way to communicate what version a piece of software (or even a document) is at. While there is no generally enforced format for versioning in software, there are several approaches to versioning. Let's go over at the most popular ones and their fields of use:
To understand the following formats and their uses, we need to first define a few words you will encounter in the rest of this post:
Major version- A primary release of a software. It is not assumed to be backwards-compatible with older versions.
Minor version- A change to the software that is backwards-compatible, like introducing new features.
Bugfix") - A change to a software that indicates no changes to the software functionality, but rather a fix of a bug or security issue.
Release Candidate") - A version of a software that is not an official release, but a premature release, for example for testing or to debug a specific issue. It may become a genuine release in the future. Commonly using the greek alphabet for numbering, e.g.
Build metadata- Meta information taken from the project or it's environment, like git commit hashes or checksums of software contents.
Different versioning formats may contain one, some, or all of these components in their version information. Depending on your software and it's intended usage, you may not need all of the information listed above to manage new versions.
The simplest form of versioning, as it only consists of a major version. You start at
v1 and simply increment the version number by one for every change (
v3, ...). While it is very easy to automatically generate new version numbers with this, the version itself doesn't give us much information about the change. Because of this, it is mostly used in places that only care if a new update is available and assume they can always blindly update to a newer version, for example desktop software, games or game assets.
Often abbreviated as "CalVer", Calendar Versioning uses dates to indicate versions. There is no one set standard for this, but the 3 most common approaches are:
YYYY.MM.DD- the actual date of the version release, for example
2022.12.01for a version released on december 1st, 20222.
YYYY.MM.PP- year and month of the release, and a patch counter, like 2016.03.11 for a major release from march 2016, with 11 patches applied.
YY.R.PP- year or the release, the number of the release within the current year, and a patch counter. For example
23.2.1would designate the second major release of year 2023, with 3 patches applied.
This approach is used for software with long update cycles that doesn't need to signal what exactly has changed, but provide the ability to stay on a major release while getting bugfixes and security updates. Well known users of this format are Ubuntu Linux and CockroachDB.
Build metadata versioning
This form of versioning is very similar to incremental versioning, in that it commonly only signals that something changed, without caring what changed, effectively treating every update like a major release. The difference between metadata and incremental versioning is the format: while incremental versioning uses an incrementing number, metadata versioning uses data either from the project itself (like the checksum of it's contents) or it's environment (like a git commit hash).
This approach is commonly used in fully-automated systems that require no knowledge of the software type or origin. A popular user of it are Docker images, using checksums of their contents as their primary version by default.
When working with dependencies in software development, fine-grained version control becomes important: You are stuck between needing the ability to immediately get security patches and bugfixes, but can't risk accidentally introducing a version that breaks your code. For these scenarios, semantic versioning is the perfect solution.
It consists of 3 main elements and 2 optional ones:
MAJOR.MINOR.PATCH-PRE-LEASE+METADATA, for example
The last 2 may be omitted. With this format come a few restrictions to make working with it predictable throughout different environments. When releasing a new version of your software, the type of change determines which part of the version identifier changes:
- If the change is not backwards-compatible, like removing a function or changing it's arguments, behaviour or return values,
MAJORmust be increased
- If the change is backwards-compatible but added features,
MINORmust be increased
- If the change did not change the api, but fixed a bug or security issue (i.e. fix something to work as intended that previously did not),
PATCHmust be increased
- If the current version is not stable, a
PRE-RELEASEidentifier must be added
- You may optionally attach build metadata, like the git commit hash the version originated from
As you can see, there are several approaches to versioning your software. From semantic versioning being the most verbose but with great benefits for dependency management to the straight-forward incremental versioning counting up for each version. Which approach and format you choose is ultimately up to you, with some lending themselves more for some use cases than others.