Thursday, July 19, 2007
Because I work as a freelancer, I get to see a lot of different .NET development shops. One of the things that continually surprises and frustrates me is how poorly many teams organise their solutions. By this I mean the way they split their application into projects (and thus assemblies), the way they group those projects into solutions, naming conventions and the way they source control those solutions.
Microsoft’s Patterns and Practices group has specific guidance about how you should organise your solutions here and you can’t go too badly wrong by following their advice. Unfortunately some of the following articles about build practices are way out of date and really should be updated, but that’s for another post, right now I want to give a brief list of do and don’ts on organising solutions.
The most important thing to get right is to understand your team and the systems it builds and maintains. What source is in your control and what is outside it. It’s very important that you don’t build artificial silos within your team that make sharing and reusing code difficult. Ideally, all the source your team writes should be in a single solution file with all the projects referenced with project references. Not doing this is the single biggest source of solution problems I’ve seen. Never use file references for internal assemblies! It’s such a headache making sure you’ve got the correct version of an internal assembly especially when it’s busy being developed.
I’ve often seen the situation when two projects in the same solution both file reference the same internal assembly, but different versions of it so when you build the solution you get errors because visual studio complains that it will have to overwrite one version of an assembly with another. Also there’s the issue of where you reference those assemblies from. If you reference the build server’s (you do have a build server right?) latest built assemblies, you can often find that your local build breaks when another developer checks in and builds a new version of the assemblies that you’re referencing. On the other hand I’ve worked on teams where I’ve had to manually maintain assemblies and file references. All this is bad and unnecessary.
When I arrive to work with your team I should be able to get the latest code from source control open the solution file and hit F5 and the build should work first time. Never ever put your stuff in the GAC. The only excuse is if you are forced to by COM interop issues. It’s always a bad decision and will give you endless build and deployment nightmares.
What about third party assemblies you reference, or assemblies from other parts of your company that your team has no control over? If it’s a .NET API to a non .NET product and you’re going to have to run its installer on your deployment target and the installer places the assembly in the GAC, then it’s probably best to just go with that. For pure .NET assemblies that can be xcopy deployed, the best think is to treat them like other binary resources and put them in your repository along with the source. You can either put them in their own solution folder or include them in each project that references them as a project item (as suggested by the P&P document above). The only problem with the latter approach is that it can be a headache when you want to move to a newer version and you have to hunt down all the projects that reference it.
OK, so we have a single solution with all the team’s projects in it. How do we name and organise those projects within a solution? As far as naming goes there’s a simple rule that will make your life much much easier: Project Name = Assembly Name = Assembly File Name = Root Namespace = Project Folder Name.
For example, say you’ve got a root namespace like this: MyCompany.MyApplication.DataAccess, then the project name should also be: MyCompany.MyApplication.DataAccess, in a folder called: MyCompany.MyApplication.DataAccess. The assembly name should be: MyCompany.MyApplication.DataAccess, and the assembly’s file name should be: MyCompany.MyApplication.DataAccess.dll
I would expect the solution name to be MyCompany.MyApplication or maybe MyCompany.TeamName if your team’s solution file holds a number of different applications. You do share code between your team’s apps don’t you? On disk you should keep things flat. The solution should go in a folder with the same name as the solution (MyCompany.MyApplication) and all the projects should go in child folders of the solution folder. This is how VS likes it and it’s pointless fighting the power.
One place where you do want to fight the power is with web projects. Don’t let VS put them in a virtual directory under WWWRoot. Create a blank solution file first, then create a folder under the solution directory for your web project. Create a virtual directory using inetmgr that points to the project folder. Last of all, create the web project and ask visual studio to put it in the virtual directory you just created. This is much easier with VS 2005 + and the problem has mostly disappeared because you can use the cassini web server to run a web project from wherever it happens to be on disk.
Your source repository hierarchy should exactly match the solution structure on disk. Not doing this is a recipe for disaster. Don’t be tempted to use the file linking feature in Source Safe to share source files between different solutions, it causes endless headaches whenever someone adds a new source file to a project, checks the project file in but doesn’t add the new file to every location the project’s linked to. As for source safe, although it’s the default option for every Microsoft shop, it’s also probably the worst SCM tool out there. Really consider using something more modern. I haven’t had the opportunity to use the new Team System source control tool, but I have used Subversion on one project and it was like moving from a Trabant to a BMW. However that one experience with Subversion was the only one in my long career as a Microsoft developer, everyone else uses Source Safe. A great pity and definitely a subject for a future post.
Posted by Mike Hadlow at 2:24 pm