Build numbers and version numbers
The Best of Both

Build numbers and version numbers

Let's question the assumption that applying the version number should be the last step in the release procedure. Why is this?

Essentially it's because we don't want to have more than one version 2.0. If a customer contacts us to report a problem with version 2.0 of our software, then we'd better be able to identify exactly what it is they're running. Indeed, if the system testers report a problem with version 2.0 of the software, we'd equally like to know which build they're talking about. If we fixed the software version at 2.0 and then continued to work on the code until it reached release quality, how would we ever identify the real version 2.0?

Note, though, that if we were prepared to allow our build server to generate a unique build number for every build it produces, and to use this as our version number, we'd have no problems. If build 1729 was the build which passed the tests, then our release could simply be identified as version 1729 — with no change required. Unfortunately version numbers aren't just numbers; conventions dictate how their fields are interpreted, and a version of 1729 flouts these conventions.

The Best of Both

The way to break out of this apparent conflict is simple. We must give up on the idea of tying the version number to the version control system: it just doesn't work. Instead, we can adapt the build number idea to automate our versioning as follows.

  1. Again, a single file — VERSION, let's say — must provide the single source of version information. This file is version controlled, but doesn't use keyword expansion [2]. Instead, it will (generally) only be modified automatically by the build system.
  2. At the start of the countdown to release 2.0, the contents of this file read 2.0.0. Here, the leading "2.0" is the major and minor part of the version number, and the trailing "0" is the build number.
  3. From this point onwards, each time the build server produces a build it uses up a build number. As a post-build step, the server edits the VERSION file to increment the build number by 1, then checks this change in, ensuring that no subsequent build can have the same version number.

We now have the build server generating a series of release candidates, each with its very own version number, 2.0.0, 2.0.1, 2.0.2, ... When a release candidate meets the required quality level it can be promoted to being a formal release. This promotion is essentially a book-keeping operation: the software may be tagged, the deliverables may be archived; but whatever happens, there should be no further change to the code, and what gets shipped will be identical to what was tested.


You'll notice that the full version of the final 2.0 release is unlikely to be 2.0.0. If it took us another 29 builds to fix all the defects, we will ship 2.0.29 — a rather less attractive number. Typically, in most cases, we should truncate the version number before presenting it to the user. You certainly won't see a press release announcing the completion of version 2.0.29!

Often, a three part version number is insufficient. It's more common to choose a number of the form, interpreted as: major version 2, minor version 2, patch number 3, build number 67.

There is a convention [3] to use odd minor version numbers for ongoing development and even version numbers for released versions. Thus, while the release team are knocking 2.0.x into shape, the development team continue with 2.1.x on the trunk.

Scripts should be written to perform all the common operations: to create release branches, to promote release candidates into full releases, and so on. It's important to have these scripts, and indeed the whole process, tested and in place well before the countdown to the final release.

[2] I also strongly recommend that keyword expansion is disabled for all files in the repository, for the reasons described in this article and also here.

[3] This convention is used by the Linux community. See Wikipedia on Software Versioning for more information.

Copyright © 2007 Thomas Guest