Wednesday, September 17, 2003 1:18 AM
Shawn A. Van Ness
Assembly Versioning Code of Ethics (or: Dipping Your Toes in the Warm, Gentle Waters of Code-Gen)
Prompted by an email on the dotnet-clr list, I took a moment to write down my Assembly Versioning Code of Ethics. These rules are not enforced by .NET or the CLR in any way -- but I think I speak for virtuous developers everywhere when I say:
I. Thou shalt bump at least the "minor" field, when making a change that might affect existing client code.
II. Thou shalt bump at least the "revision" field, when making any other change, whatsoever.
I hate to make an example out of anyone, but a certain SDK team at MS has broken both of these simple commandments over the past 18 months, and each time it's caused quite a lot of frustration.
Post RTM, the Tablet PC team has published one additional point-release of their SDK. They bumped the version number on the Microsoft.Ink.dll assembly (the managed wrapper for the Tablet API) from v1.0.2201.0 to v1.0.2201.2. Now, I don't know what happened to the intervening 2201.1 release, but still, the small hop from 2201.0 to 2201.2 certainly looks like a innocuous little bug-fix patch, right?
But no -- they've added IDisposable implementations to about half a dozen of the most heavily-used classes, and updated the documentation to imply that if we don't call Dispose() then our apps will leak -- that's a breaking change, darnnit!
Prior to the RTM release of the SDK, they were guilty of the second sin, as well: distributing multiple versions of the assembly, all under the v1.0.2201.0 label. It was unfeasibly difficult to distinguish between them "in the field" (eg: on a coworker's misbehaving test box). These doppelganger assemblies were just internal alpha and/or beta releases, but still, such needless confusion -- why?
I know why. In fact, I'm guessing this story sounds painfully familiar to a lot of readers out there. The problem is human: it's a royal p.i.t.a. for developers to remember to bump the revision number, by hand, with each build they send off to the QA department. In the unmanaged world, binaries were (usually) not signed, and so details like version-info could be patched up in the very last build stages, by setup/integration engineers whose jobs it is to think about these things. Versioning is too important to rely on 100% unfailing compliance from 100% of all developers on your team.
The good news: with just five minutes of effort, you can automate this problem out of existence!
I've become fond of auto-generating an AssemblyVersion.cs file, pre-build, with the low-order 32 bits of the version number (the build- and revision-fields) based loosely on the current date and time.
For example: if I released a new product right now, I might stamp it v1.0.2003.916... Or maybe even v1.0.2003.9169 -- where the last digit (9) is computed something like (10*DateTime.Now.Hour/24). We only have 16 bits per field to work with, so we mustn't go crazy trying to squeeze in a full-
fidelity timestamp -- it won't happen, and it's not worth sacrificing the readability of the build-date in the version.
Some might be tempted to go further down the "version == build-time" road, eg: v2003.9.16.2315... but that doesn't easily let one release, say, a new security patch for a product that's one or more revs past its prime.
Why not? In .NET, version numbers are comparable (in the System.IComparable sense) so it's not cool to release a new patch for last year's code, which compares as anywhere close to the same patch for the current generation product. This is not just academic: someday, you or your users may wish to apply binding-redirect policies to your software, based on declared ranges of version numbers! Reserving the high-order fields in the version number to represent a logical product release is a huge help, in that case.
Besides, there's an alternative available: If you really want to stamp your assemblies with a human-readable, full-fidelity build timestamp, check out the AssemblyInformationalVersion attribute, which allows you to supply a whole string, of your own devising (eg: DateTime.Now.ToString() ;-).
Here's the X-Code .NET template I use to generate an AssemblyVersion.cs file, for all my C# projects. (This technique, if not the exact template code, should be easily portable to CodeSmith or other template-based preprocessors.) The end result? I sleep soundly at night, knowing that my build environment will automatically and unfailingly increment my assembly version number, every few hours.
var now:DateTime = DateTime.Now;
var build:short = (short)(now.Year);
var revision:short = (short)(now.Month*1000 + now.Day*10 + now.Hour*10/24);
"<%= major %>.<%= minor %>.<%= build %>.<%= revision %>"