Versioning your software is very important. Most people, and by people I mean engineers, assume they know how versioning works and in a lot of cases they are correct. However, it can't hurt to deep dive into how it works and why it works when it's done right.

In case it wasn't clear when I talk about versioning I am not talking about version control system like git, but rather the version that is stamped on your software when it is distributed.

There are many different ways to version your software. There is no right or wrong way, but for most software semantic versioning make a lot of sense, and that's what I'll be focused on in this article.

Semantic Versioning 1.0.1

When you see a version like 1.2.3 it has three distinct parts. These are the major, minor and patch versions respectively. Each one has its own rules about when and why it should be incremented.

Increment the patch when you are fixing bugs, but not changing any functionality. Applications that rely on your software should always be able to upgrade a patch release without breaking.

Increment the minor when you are adding non-breaking features. That is, you are providing more features, APIs, etc to your application without changing any existing functionality. Other applications that rely on your software should be able to upgrade, but not necessarily automatically. It may require them to make very minimal changes.

Increment the major when there are breaking changes. Applications that rely on your software might work, but as general rule you expect they will not. Only in cases where other applications use features that have not changed in the new major release might not need to updated.

The version must always have all three parts, even if they must be padded with zeros. For example 1.0.0 is a correct semantic version, 1.0 is not. This is a rule most people do not follow, but it is important for a few reasons:

  1. It's easier for regular expressions to understand. Whenever you have build systems or package managers that rely on comparing or sorting versions.
  2. It's explicit and consistent. Anyone reading the version is able to assume that it's a semantic version and that parts of the version are given and not ambiguous. For example 1.5 may refer to all patch releases, 1.5.0 means it's specifically the first release with no patches.
  3. It's part of the semantic versioning specification. Most likely because of the reasons above.

This is how it should work, in theory. If you are able to stick to this regiment then your software will be well versioned and understood by all.

Prereleases and Build Numbers

The basic rules for semantic versioning work great for stamping versions that go to production. However, there are many cases when you want to version alpha/beta releases, or even independently version branches of your application. For these scenarios there are two further rules in the specification:

  1. A version may contain an optional label prefixed by a -. This looks like 1.2.3-alpha
  2. The label itself is versioned with an integer immediately after. This looks like 1.2.3-beta5

alpha and beta are common labels that are also recognised by most package managers for what they are.

You can also use this label to indicate a branch of your software. Whilst developing and testing new features for you application in parallel the internal versions may looks like:

  • 1.3.0-customers3 is the third iteration to test the customers feature.
  • 1.3.0-suppliers1 is the first iteration to test the suppliers feature. You could also use 1.3.0-suppliers, but how many features have you written that make it through QA in the first round and goto production after that?

When one (or several) of these features are completed it will be merged in as 1.4.0.