In a previous article, I discussed an efficient method for organizing your source code. Well, having employed the model myself for a few months now, I can officially say that this turned out to not be the most ideal situation. So, this time around I'm going to discuss Interscape's modified method for managing source code, and show you why it is more efficient than before and why it has become a tool that also assists in maintaining that code.
In my last article, I talked about keeping your source code tree organized by splitting the codebase for each version of the framework. This is fine for really small projects, but does not scale well for large projects with lots of iterations, or for source trees with lots of products in them. For example, Interscape's source tree currently has 12 major products, many of which are now in their second and third iterations. And each one has two branches for each version of the framework. It did not take long for this to spiral out of control.
After experimenting for several hours, I finally came up with the perfect way to manage the company source code while maintaining as few duplicate files as possible. As it turns out, part of the problem was that we were not using our assembly versioning methodology correctly in our source control. It's a perfect example to show how a great methodology can go wrong if not thought out completely, and how what may work conceptually may not work in practice.
Problem #1: Conditional compilation, dual codebases
I can now safely say that using dual codebases is a 100-percent horrible idea. What was worse is that we were using conditional compilation syntax in the code itself, so the fact that there were two copies of the code did nothing but waste time and space. It was totally unnecessary to copy the code files, because we determined that different versions of the VS.NET solutions and project files can be bound to the same source control. Therefore, the only files that need to be different are the *.sln and the *.xxproj files.
Problem #2: Incorrect version branch naming
In my previous article, I said that you should name your branches based on the AssemblyVersion attribute, which included the .NET Framework version for easy compatibility identification. Not only is this incorrect when dealing with a unified codebase, but it is also impractical in a decent-sized environment. The smarter move, since you're working from a unified codebase now, is to name each version branch with whatever you would use for the AssemblyVersion attribute.
So now, my recommended Tree structure is as follows:
You can see this structure at work in Figure A, a screenshot of the Interscape Source Tree.
|Interscape Source Tree|
Now that we're dealing with a unified codebase, how do we deal with the different versions of the VS.NET Solution System? Well, for a while, I had to manually duplicate and modify the text configuration files. If you know the version numbers of each configuration schema, it's not a big deal at all. However, when you start dealing with seven or eight solution and project files, it tends to get fairly cumbersome. So I built an application that I call the VS.NET Version Commander. The main part of the application looks like Figure B:
Basically, it takes any project or solution file and creates two new files, targeted to the proper versions, and renamed based on settings you specify on the Options menu. If you give it a solution file, it will optionally look for all the projects referenced by the solution file, and attempt the same adjustment. It won't touch the existing file, so you don't have to worry about losing your work. It just creates two brand new files in the VS.NET file type. Once it is finished, you just check the new files and your code into source control, and bind the solution and project files to your source control tree. Then, you're good to go!
Optionally, the Version Commander can clean out any existing version control declarations and change any hard-coded, version-specific assembly bindings. (A couple times I neglected to change an assembly reference from 1.1 to 1.0, and I had a 1.0 assembly that was still dependent on 1.1 Framework assemblies. Not good.) Finally, it will optionally add the NET11=True conditional compilation directive to VS.NET 2003 projects, to take advantage of the assembly versioning policy discussed in a previous article.
This method has proven very effective over the past month, and should last Interscape a good long time. And the VS.NET Version Commander does a fabulous job automating the entire process. It used to take me about 10 minutes to make sure each VS.NET file was formatted to the proper version. Now I can work in VS.NET 2003 all the time, and still have my source code organized the way I want it to be.