In Eliminating Failed Deployments – Part 2 – Automate Your Obsession, one of the checks I suggested was:
Ensure all of the binary files have an appropriate version number; “126.96.36.199” is NOT an appropriate version number.
So, WHY isn’t 188.8.131.52, or anything like that, an appropriate version?
It’s because it’s artificial – a dummy value that’s normally used because the developers haven’t implemented automatic versioning on the build system. If you’re in this situation you need to change it. If you need reasons, there are several:
Optimism – It Can Make Things Go Right!
Both Windows and Linux are designed to handle versioned binary files and distribution packages and installers very well. If you try to install a new version of something over an older version, the system handles everything for you.
If you try to install something with the same version number as the package that is already installed, Windows at least will start complaining, and you have to work harder to upgrade the files. If you’re using continuous integration, continuous deployment and/or DevOps, you don’t want to do anything that makes deployments more difficult.
Incrementing the version number of the files makes your deployment process easier and more reliable – certainly a couple of arguments in its favour.
I’ve spoken to people who don’t see these as reasons to increment version numbers with each release, but their arguments have never really seemed convincing. Even if you recreate a VM image with every release, you still need to know where your code came from.
Unique version numbers also give you greater traceability, which is always good. Without that, you get the problem described in the next section.
Pessimism – It’s Going To Go Wrong!
Imagine you have a bug reported in your production system. You trace it to one subsystem, and then decide that you need to make a change to the source code to fix it. How do you know where to find the source code for that version so that you can change it? There’s a very good chance the version in production isn’t the latest version in your source control system. You might have to dig round in the archive until you find the right version.
It’s a pain, but if every build has a unique version, and your source control is labelled or identified in some other way, it is at least possible.
Now imagine everything is version 184.108.40.206. How will you find the right version of the source code? You may be lucky and be able to see the date the system was compiled. As long as your CI system didn’t produce lots of builds around the same time, and assuming there isn’t a time zone difference involved anywhere to add to the confusion.
You may be able to get some clues from labels in the version control system, but that may or may not work.
If you get really desperate you may even have to resort to decompiling executables and libraries, as long as they’re in a language where you can actually decompile them to something resembling the original source code. Then you can compare the decompiled executables and libraries to various versions of your source code, all the while wondering where you went wrong to get saddled with sorting out this mess. If you can’t decompile the binary files then you’ll just have to make an informed guess about the version of source code you need.
This is a situation that should never happen in a professional environment, under any circumstances. That doesn’t mean it doesn’t happen, but it shouldn’t.
Realism – It’s Not Difficult!
To implement automatic versioning in your build system you need to do the following:
- Obtain the current build number. This is something that the build server (certainly Jenkins and TeamCity) will provide easily, usually as a macro or variable that you can use in command lines or scripts.
- Update the version number in your source code. Ideally this would be in one place, but it may be in several especially if you’re developing in several languages.
- Carry out the build for your system
- Update the version control system with the latest version number – updating the actual source files or adding a label will work.
You’ll probably have to do a little scripting to do this, but there are plenty of examples of how to do this freely available on the web. It’s only got to be done once per project, and the scripts generally don’t change.
To simplify things even more, if you’re using C# and you have a solution comprising multiple projects, you could remove the common information from all of the
AssemblyInfo.cs files (Company details, copyright, version specifications etc.) and place it in a new file called
CommonAssemblyInfo.cs in the same folder as the solution. Add this file to each project as a link, NOT a source file. Once you’ve done this, any changes to
CommonAssemblyInfo.cs will naturally appear in all of the modules.
Practicality – Choosing The Version Numbers
I mentioned above that build systems provide a build number that can be accessed quite easily, but that’s only one number. Where do the others come from, for example in the four part version numbers used by windows?
The documentation describing the way to version assemblies, on the MSDN at Assembly Versioning says that the structure is:
<major version>.<minor version>.<build number>.<revision>
There are two version numbers in .NET, AssemblyVersion and AssemblyFileVersion. The differences between them are explained at: How to use Assembly Version and Assembly File Version.
Alternatively for APIs the SemVer2 standard at https://semver.org is straightforward, logical and has helped at least one of my friends’ departments pass a development audit. It’s definitely worth considering.
The Python 3 script below can be run from the command prompt or from within a build system and will set the build numbers in the AssemblyInfo.cs or CommonAssemblyInfo.cs files in a given directory and its subdirectories:
From a Windows command line, if you want to set the versions in the files in the directory "E:\Articles\Examples\Versioning" to 7434 this script can be run as:
python updateversions.py "E:\Articles\Examples\Versioning" 7434
python updateversions.py -h
will display the required and optional parameters for the script.This is only a basic script and, as I’m not exactly a Python expert, it took a few hours to write. It works though, and if you’d like to use it you’re welcome. I accept no responsibility if it doesn’t work, but if it does then please let me know.
The script shows that the small amount of effort it takes to implement automatically updated version numbers, and the advantages it gives, far outweighs the disadvantages of not bothering.