Software versioning explained

Table of contents

Version numbers have existed for a long time, and you have probably seen things like v1, 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:

Versioning vocabulary

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.
  • Patch (aka "Bugfix") - A change to a software that indicates no changes to the software functionality, but rather a fix of a bug or security issue.
  • Pre-release (aka "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. Alpha, Beta etc.
  • 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.

Incremental versioning

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 (v2, 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.

Calendar versioning

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.01 for 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.1 would 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.

Semantic versioning

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 1.1.0 or 2.1.8-alpha1.

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, MAJOR must be increased
  • If the change is backwards-compatible but added features, MINOR must 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), PATCH must be increased
  • If the current version is not stable, a PRE-RELEASE identifier must be added
  • You may optionally attach build metadata, like the git commit hash the version originated from

Semantic versioning has the huge advantage that it allows dependencies to be updated automatically, because the version identifier contains all the information required to do so safely, without breaking anything (aka update only to newer minor/patch releases, not pre-releases or major versions). Because of this, it is widely used to version dependencies and libraries, like modules in go, python or javascript.


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.

More articles

Managing tickets with discord forums and PHP

More flexible automation with Discord's forum channels

Why bash shell aliases deserve your attention

A seriously underrated feature of the bash shell

Automating code quality in go

Exploring what code quality is, tools to maintain it in go and how to automate them

Common pitfalls running docker in production

Avoiding the mistakes many make when embracing containerized deployments

Modern linux networking basics

Getting started with systemd-networkd, NetworkManager and the iproute2 suite

Understanding how RAID 5 works

Striking a balance between fault tolerance, speed and usable disk space