I’ll start by admitting that when I came to DNN module development I did not have a very profound vision of version numbering. For me version numbers were simply an indication along the lines of “version 1.2 is better than 1.0”. But DNN has a very rigid xx.yy.zz scheme for both it's own as well as module version numbers which meant that I had to pay attention to this. 7 years later it has become quite a hobby horse of mine and I thought that this holiday season I should let this hobby horse roam a bit.
Let’s begin by exploring what the version nr is for. Well, there are actually several stakeholders here: you (the module developer), your customer or anyone else using your work, the DotNetNuke framework in which it gets installed and finally .Net. We’ll look at each in turn.
Internal value of version numbers
Obviously you will want to have some way of identifying what goes out the door so that you know what a customer is running. But there can be other reasons, too. Many module developers use it to make money. Release a new major version and you can ask your (existing) customers to fork out for it. Snowcovered played a big part in making this a popular approach, IMO. The way it worked, with the ability to set discounts for customers of package XYZ, it made it easy to follow this pattern. Other than this there’s not much to say about what the number means (or should mean) to you as developer and it is not the main focus of this article anyway.
External value of version numbers
When you release a new version of your software and it has a version number you’re also implicitly communicating something to your audience. This has to do with a common practice with the xx.yy.zz numbering scheme. Formally this is identified as major.minor.build number. So DNN 5.0.0 was a “major” release, 5.4.0 a “minor” release and 5.4.4 a “build” release. Unfortunately here is where confusion starts as quite often a minor release will be referred to as a major release and a build release (which sounds awkward and geeky) will be called a minor release. What is happening is that the first two digits of the version nr are being lumped together and we just use a distinction between major (5.0 or 5.4 alike) and minor (5.4.4) releases. A more correct term IMO is “bugfix” release for a build release. But that hooks in to the following point: what can a customer expect for a release? Well, naturally the more “major” a release is the more “major” the changes. You’re best of accepting and adhering to this as this is just a nobrainer. Changes from 5.3.3 to 5.4.0 are more significant than from 5.4.0 to 5.4.1.
But what constitutes a “major” change? Ultimately it is in the eye of the beholder. If you do some fancy refactoring and your users don’t notice a thing in the UI, you might think it is a major release, but your customers probably won’t. So it is not always as simple as one might think. DNN 5 had major architectural changes under the hood. But in the front end it was not all that different to users. This, in part, explains why not many users migrated at first. What does the end user care that the installer has been completely rewritten? Not much. In the end you’ll need to make the case that the user gets a major upgrade with the major new version.
One point of criticism at the Snowcovered way of doing upgrades is that it encourages developers to label their module as a major upgrade in order for them to make money. This is purely my point of view, but I felt that at times it is equally valuable to spend time on stabilizing your product and going through a prolonged set of bugfix releases. IMO there should be no external pressure on developers to label a release differently then what is technically accurate. That I put in bold as I think this is definitely not always the case in practice. Sometimes a marketing department forces a version number onto a product that is not an accurate reflection of what happened. For my own Document Exchange I changed the business model to subscription-based in 2007 to help with this … and still it is not always easy to stay disciplined with numbering. So ideally a major change should constitute a major change in functionality for the users (admin or end users). And ideally the bugfix releases should limit themselves to attempting to fix bugs and not creep in new features. OK, we all violate against this from time to time, but I assume we all underwrite the principle.
One thing to keep in mind is that there are most likely customers that make custom solutions that leverage your product in some way. This is great and I’ve always encouraged this. In general there are several ways in which this is done: by leveraging an “official” public part of your module (e.g. a web service), by leveraging the API of your module (i.e. loading the dll in your project and using the API), and finally by going straight to the (SQL) data. These are listed in order of power and risk. You can do virtually anything by going straight to the data but it also has most breakage risk. One thing I’ve tried to stick to is to not change the data model or the API during bugfix releases. Again I don’t think it is always possible, but one should definitely attempt to remain disciplined here. In DNN 5.4.3 a method was removed from the Data provider. The method was not used anywhere in DNN itself. But unknown to the programmer that removed it, quite a few module developers used it in their code. So the release caused all kinds of mayhem. From my point of view the biggest mistake was that it happened during a bugfix release. If done for a major release then fair enough. We can’t expect DNN to be cast in concrete. But for a bugfix release one should avoid touching the API.
Value of the version number to DotNetNuke
For those familiar with module development, you’ll know this already. DotNetNuke uses the version number during the upgrade process to determine what needs to be done to upgrade the module. The most obvious place where this happens is the database scripting. The developer will package a module with a set of SQL scripts which DNN runs upon install. The first script that is run is the one with the lowest number or the one that is preceded by “Install”. Then all higher version scripts are run in turn. Upon upgrade the installer looks at the current version of your module and runs only those scripts preceded by a higher version number. This is a wonderful mechanism and IMO is one of those key aspects that has led to such a successful third party extension market around DNN. Just one thing: DNN likes numbers as xx.yy.zz for this. So stick to this. You won’t be able to use xx.yy.zz.aaa numbers for instance. Stick to the convention and your module will upgrade smoothly with your customers.
Another place where it is used by the installer is for cleanup files (.txt files with a list of files to delete) although that has been deprecated for DNN 5.
Value of the version number to .Net
This is where things get more technical. Disclaimer: although I have done quite a bit of research and work in this domain, it is quite vast and I am aware I don’t know everything there is to know about this. I’ll attempt to explain what I know and hope it is to your advantage. Asp.net loads the various assemblies from your bin folder (and caches them). Upon compile an assembly is labelled with its dependencies. So the dll of a module developed on DNN 4.6.2 will get an internal label telling asp.net that it depends on DotNetNuke.dll version 126.96.36.199. This number is the version that the actual dll is decorated with (I’m still confused as to whether this is the file, assembly or product version). The important thing to realize here though is that asp.net will refuse to load/run your dll if the version of the corresponding dll in the bin folder (so we’re talking about the actual installation at your customer) is lower than the one you compiled against. So the module developed on DNN 4.6.2 will plain refuse to run on DNN 4.5.5 even though you may not use any methods that are different between the two versions. This mechanism is the cause of all the issues surrounding shared libraries. If you compile using a Telerik version higher than the one in the general DNN distribution, your customers will get a broken module! Although there was a way to list such dependencies in the DNN 3 manifest, AFAIK this has been deprecated in the new DNN 5 installer. Maybe this post will provoke the powers that be to reintroduce it.
But there’s not just the dependency on other dlls to take care of. Your dll sits there in a chain (or web) of dlls and other dlls may depend on yours. This raises the issue of correct manifest labelling of your dll. In your project you will have an Assembly.vb/cs. This holds the meta data that other projects use to refer to your dll. In my build automation tools (based on Nant) I automatically distribute the module version number to its dlls. Not only is it “good practice” to label your dlls correctly. It is also a great way to determine what is actually running on the client’s server. Often, version issues will appear in error logs with the version as it is read by asp.net. Also you can delve into the bin folder and get Windows to tell you what version a particular dll is. This has helped me numerous times in debugging complex situations. So please: keep the label in sync with the project version. You’ll do yourself and all of us a favour.
Figure 1. Setting version nr in Assembly Information on Project Properties page of VS
Figure 2. AssemblyInfo.vb contents. Note there are more properties surrounding assembly versions. Read more here.
Figure 2. Example of dll properties in Windows Explorer
How you do things is up to you. I’ve attempted to gather a number of items that serve to help you guide your decisions regarding version numbering. Happy programming.