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.
I've seen the same thing where I work. Couldn't agree with you more!
ReplyDeleteStructuring solutions is always an headache but thanks to your post it won't be anymore (At least not a big one...a bit smaller...lol). Cheers!^_^
ReplyDeleteThanks orangeeli, I'm glad it was useful.
ReplyDeleteNot so sure that CompanyName should have such a central place in the whole structure. The company can change its name or be bought (happened to us several times in a row) and it will be a big headache to change all file/assembly names, update installation/documentation. It's fine when it's just a namespace where we can keep whatever names we want, but exposing company name through file/assembly names may be a questionable practice (unless you are Microsoft).
ReplyDeleteThanks anonymous, that's a good point. Your root namespace shouldn't be something that's likely to change frequently. I guess it's a judgment call, if you work for Microsoft, then it's quite a safe bet to start your namespace with 'Microsoft', but if, as in your experience, your company is changing hands and/or name you'd probably want to use something else. But the central point still stands, you need to use a namespace that's unlikely to be duplicated elsewhere to uniquely identify your code.
ReplyDeleteA great article!
ReplyDeleteI've printed it and I'll show it to my colleagues.
We use SourceSafe 6 with VS2005 and I don't like it.
At home I always use TortoiseSVN (http://tortoisesvn.tigris.org/) and it's much easier.
Paul, glad you found it useful. I'm a bit fan of TortoiseSVN too. It's a shame (but understandable) why more shops are not willing to try something other than Source Safe.
ReplyDeleteSolutions and projects are indeed confusing. I found a good explanation at http://progproj.net/?p=76. Although it discusses VC++ it's the same concepts.
ReplyDeleteIt all works well for relatively small projects. Try a hundred developers over several years and many releases. Single solution approach simply does not work. Every time you modify one single file it will compile for minutes even on very good hardware. How many time do you compile per hour? I bet it's at least half a dozen. Slows development to a crawl.
ReplyDeleteNo other way but to split into multiple solutions and reference assemblies from a single shared folder. :-(
Hi Anonymous,
ReplyDelete100 developers? Over several years? That's either seriously hard core, or seriously FUBAR :)
How many million lines of code do you have? How many projects?
Great comments....but it would be great if you could reformat your article into logical paragraphs....make it MUCH easier to read and benefit from your pearls of wisdom. I keep getting lost in that enormous paragraph when I try to review what you've said.
ReplyDeleteAnonymous, it was formatted. Something went horribly wrong when the new blogger arrived... and I'm too lazy to fix it now :(
ReplyDeleteThanks for your post - I came across it because we are surely one of those companies who do not use, let alone understand VS as well as we should. Your article did provide some sense to things, but no question, this is the worst source control monster I have ever experienced.
ReplyDeleteI did however find it interesting and maybe a bit poetic that you recommend "dont fight the power" - in other words, just try to conform to Microsoft's 'way' of doing things.
To me, thats like telling the Iraqi people to just 'get over it' and live with Saddam Hussein - but like them, I dont have much choice. I have to use VS even though its an overly-complex, horribly organized, poorly documented, and confoundingly clunky system. Hmmm... Just like the former Saddam!
Someone please pray for me, or even better - just shoot me!
Sure it's a great post, one of those useful and worth of reading. I was trying to figure out this dilemma of VS project org and definitely your article addresses most of my doubts. Now from then, it's a valuable foundation for "power be with you" projects organization and understand those already found in the shop where I work for. Thanks a lot!!! I hope you to continue seeing such a good posts.
ReplyDeleteYeah, I happen to agree with a previous commenter. The post is about structuring VS, but how about structuring your blog post by using multiple paragraphs?
ReplyDeleteIt's really hard to skim and find what you want when it's packed so densely.
Just sayin'.
Hi Jeffrey, Thanks for kicking me to reformat this, it's old, but still quite popular.
ReplyDeleteHoly cow! You took my suggestion! (and at least one other...)
ReplyDeleteIt looks great. Thanks for the cleanup.
Great post. Great job highlighting the "Project Name = Assembly Name = Assembly File Name = Root Namespace = Project Folder Name" rule.
ReplyDeleteAny 2011 tips on working with Team Foundation Server?
In particular, I am trying to rationalize your recommendation with a Microsoft Patterns & Practices recommendation. You recommended "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." MSFT P&P recommends a "flat structure" in the "partitioned solution" section at http://msdn.microsoft.com/en-us/library/bb668953.aspx. I would retain your dot-rich / namespace matching naming for the .sln files.
My head might be spinning, but I think your recommendations are for a single application and the MSFT P&P recommendation is for multiple applications with shared class libraries. I think I am just trying to extend all of your recommendations to this case of N apps sharing M class libraries.
Does the MSFT P&P flat structure modification to your recommendations kill any of the benefits of your recommendations?
That is, can I follow the MSFT P&P flat structure recommendation for "partitioned solutions" but still retain all the benefits of your various recommendations? Will this convergence of the 2 recommendations work? Will it work with TFS?
Just scouting, haven't tried this yet...
Hi Dave,
ReplyDeleteJust reading the P&P document, it looks like they are saying the same thing as me, the folder structure is identical to my recommendation, they just don't go into detail about the solution name being 'company.product'. But if you work for a small company with a single code base that contains several products, you might well have a solution named 'company'.
Having a single uber solution as the basis for your CI build, but smaller ones for working with visual studio, is a pattern I've often used. It's a very useful approach. It's especially useful if you have many products and a single code base.
Sorry Dave, I didn't answer your last question. Yes, this layout works fine with TFS, I've done several major projects using TFS with just this structure.
ReplyDeleteSo how have you found the one solution’s for multiple products? I thought it might be overkill to have all the products in one solution due to size, build time and source control dependencies between versions of different products.
ReplyDeleteWe currently have three different products in three different solutions managed in SVN as three different repositories, this allows us to tag releases independent of each product.
Downside is that we have duplicated code. The fix for that would be to have a fourth solution with common code and reference that by file which is completely painful or merge all projects into one and loose flexibility with branching versions between products.
What are your thoughts?
Thanks for the article. I am very new to .net development and am working on my first project at work.
ReplyDeleteI appreciate your article and guidance.
Aaron
Very interesting post. Simple and to the point. I agree with most of it I think, though there are a few scenarios in our company in which I end up preferring a different structure sometimes.
ReplyDeleteFor instance, we have projects with the same name, targeting different platforms (like desktop and Silverlight). In that case, I tend to group them both in the same folder, and have nested folders representing the platform, like this:
Company.Product.Component
Silverlight
Company.Product.Component.Silverlight.csproj
Desktop
Company.Product.Component.Desktop.csproj
Then, I create different solutions by platform, on the root. This is to enable TFS build to differentiate between the platforms based on the solution name.
Hi Mike,
ReplyDeleteI agree with everything you have said.
I argue similarly for adoption of Git in a Microsoft environment here:
http://jenkinsheaven.blogspot.com.au/2013/08/automating-net-builds-its-not-all-about.html