Drama-Free Version Numbers

The Wikipedia entry for software versioning is an amusing read, as it is littered with [clarification needed] tags and even a [dubious] tag. The editors that left those tags behind neatly capture my feelings about traditional version numbering schemes – they tend to be unclear, arbitrary, and quite dubious in terms of their usefulness.

picture-drama-306215-m

In traditional schemes, version numbers are used to indicate the “importance” of a release. So, increasing the version number from 1.0 to 1.1 indicates a “smaller” release than increasing it to 2.0. The problem with this approach is that the relative “size” of a change or “importance” of a release is a totally subjective measure, and it is hard to keep the scale consistent over time. For example, consider the version numbers that have been used for releases of the .NET framework:

  • .NET 1.0
  • .NET 1.1
  • .NET 2.0
  • .NET 3.0
  • .NET 3.5
  • .NET 4.0
  • .NET 4.5
  • .NET 4.5.1

What is the rationale for this sequence of version numbers? New side-by side deployments of the framework were introduced with 1.0, 1.1, 2.0, and 4.0. Versions 3.0 and 3.5 were actually just packs of add-on libraries that didn’t change the 2.0 framework. The most recent versions, 4.5 and 4.5.1, are complete in-place replacements of the 4.0 framework. Given that, I would expect 1.0, 1.1, 2.0, and 4.0 to be “major” versions, and the intermediate releases to be “minor” versions, but that isn’t how Microsoft lined things up. In the end, the .NET version numbers end up conveying no useful information beyond “here are some different versions of the framework.”

Version numbers also have a tendency to attract marketing people – as we’ve seen over the unfortunate history of Windows version “numbers” (I don’t think “XP” and “Vista” qualify as numbers). Luckily, our focus is on proprietary enterprise software development, so shrink-wrap marketing concerns should not apply to us.

So, left to our own devices, what do we want from our version numbers?

Version numbers should follow an objective convention that is not subject to judgment calls.

Time spent deciding whether a release warrants an increment of the major version number or just an increment of the minor version number is completely wasted. Instead, adopt a rote algorithm for assigning version numbers to releases. For example: always increment the minor version number when you cut a new release branch, and increment the major number with every 10th branch. Because you are releasing your software frequently, the concept of a “major” release should be anathema to you anyway – the software changes slowly and incrementally with every release.

Version numbers should monotonically increase as the code line evolves.

If there are two versions of your software in production, 1.0 and 2.0, it should be safe to assume that 2.0 was created from newer code than 1.0. Likewise, version 1.1 must have been built on code newer than 1.0, but older than 2.0 (though 1.1 may have been built after 2.0 if it is based on a patch to 1.0’s release branch). To help ensure that your version numbers always increase with each build, it is helpful to utilize your build system to automatically integrate build counters and/or source control revision numbers into your numbering convention.

Version numbers should convey useful and relevant information.

Ideally, your version numbers should contain enough information to identify the code used to create the binary and/or the build record in your continuous integration system. This allows anyone on the team to easily identify where a particular binary came from. This ability can come in handy when the need arises to validate deployments or determine the progeny of a “lost” application that you forgot was deployed to production.

My favorite version numbering convention.

I will share with you the convention I like to use – I think it does a good job of hitting the requirements outlined above. The convention utilizes all four version numbers we have at our disposal.

  • The first two numbers are used to denote the date on which the release branch was cut from the main line, in YY.MMDD format. Main line builds have 0.0 as the first two numbers, so that main line builds stick out like a sore thumb if they are deployed to production (which they should not be).
  • The third number increments with each build created on the branch. Most continuous integration systems expose this number as a “build id” or “build counter” of some sort. You can think of this as the “patch” number, as bug fixes patched to the release branch will trigger a new build, and will hence trigger this number to increment.
  • The fourth number is the source control revision number of the code used to generate the build. Using this number is only possible in centralized version control systems where there is a sequentially incrementing revision number (sorry, git users). If you can embed this, it provides yet another hook that identifies the code that went into the build.

This convention is objective and simple to implement, provides a monotonically increasing sequence of numbers, and conveys useful information. To automate it, consider building version “patching” functionality into your build system (it is a simple matter of determining the version number and then overwriting all the AssemblyVersion and AssemblyFileVersion attributes you can find). Alternatively, you can cheat and use TeamCity’s AssemblyInfo Patcher. By automating the version numbering convention via your build system, your developers will no longer have to spend time manually incrementing version numbers in code.

Don’t let your version numbers get overly dramatic. They’re just not worth the trouble.

  1. WARNING: When using the scheme of sequentially incrementing revision numbers (e.g. Subversion), AssemblyVersion will only accept values up to 65535. You’d be better served using your build system’s build counter and stamping the source code control value into a free form string field like AssemblyTrademark.

Leave a Reply

Your email address will not be published. Required fields are marked *