It seems to be a law of nature that the number of projects in a Visual Studio solution always increases over time (I’m pretty sure this has something to do with the second law of thermodynamics). This makes sense, because it is natural to create new code and new projects as a by-product of adding new features. On the other hand, it takes extra work to fully root out old code and assemblies as old features are deprecated (and this extra work is of no immediate value to the business). To make matters worse, we developers tend to create more assemblies than we really need.
Why should we worry about this? There are two basic reasons:
- Compile times scale with the number of assemblies. 100 code files will compile faster if they are in one assembly as opposed to being in 100 separate assemblies. If we are going to have any hope of keeping our compile times reasonable we need to keep our assembly count reasonable.
- In the same way that a function or class with too many lines is a “code smell”, a solution with too many assemblies is an “architecture smell”. Having too many assemblies may indicate that your application has too many dependencies, or that the dependencies are not well-organized.
To fight this battle against assembly bloat, we must first understand the enemy. Why do we need assemblies anyway? As the smallest unit of deployable software in .NET, the assembly is the primary vehicle for code re-use. If functionality in application A would be useful to application B, that functionality should live in an assembly that both can reference. We need assemblies so that we can share code and manage dependencies. If you work for a firm that only produces one product (and will never produce another), there is no compelling reason to split it into multiple assemblies.
Resist the urge to use assemblies for organization and taxonomy. Use namespaces and folders for this, not assemblies (Utilities.Time and Utilities.Threading should probably be sub-folders of a Utilities assembly, not separate assemblies). When considering creating a new a assembly, your goal should be twofold – strive to minimize the number of assemblies while also minimizing the amount of code in the assembly that goes unused in any application that references it. Obviously, these are somewhat contradictory objectives, and it is your judgment call to strike a balance between them. When trying to decide what assembly a new class belongs in, we suggest using the following procedure:
- Find the set of assemblies that already have all the references that the class in question requires. Call this set the candidates.
- Find the set of assemblies that need to depend on the class in question. Call this set the dependents.
- Of the candidates, find the set off assemblies that all the dependents already depend on. Call this set the finalists.
- If the finalists set is not empty, choose the assembly from that set that, in your judgment, is the best fit for the class in question.
- If there are no finalists, you can either put the class in a new assembly, or you can add it to one of the candidates. In either case, you will have to add a reference to this assembly in all the dependents.
You can also approach the problem from the perspective of having existing assemblies and wanting to consolidate them. If you have a proposed consolidation, the question to ask yourself is: will these two assemblies ever be separate? If assembly A depends on assembly B, are there any circumstances where you would use B without A? If no, they don’t need to be separate assemblies.
To reiterate, when assigning your classes to assemblies, think about dependencies and deployment, not taxonomy and organization. Doing so will prevent you from creating too many assemblies in the first place. Also, as your code evolves, continue to re-evaluate your assembly breakdown based on the goal of minimizing the number of assemblies and the amount of unused code in your applications. Remove code as it becomes deprecated, and take advantage of opportunities to delete or consolidate assemblies. Try to make sure that a new developer on your team won’t be shell shocked by the huge number of assemblies in your solution.