Developing Application-Wide with NDepend
When an application reaches a certain amount of code, the application code gets usually partitioned amongst several Visual Studio solutions. Visual Studio ends up slowing down, especially if it hosts several addins. Above a certain volume of code in the opened Visual Studio solution, the IDE becomes quasi-impracticable. The volume threshold naturally depends on the underlying hardware. For example, on a modern 2000US$ laptop (64bits dual core + SSD + 8GB RAM + Wnd8) one can experiment 20 seconds to open a 50.000 Lines of Code solution. The compile-all time mostly depends on how VS projects are organized and referenced, but if it is more than 10 seconds one can consider it as a major burden.
So code volume can be a reason to split an application code amongst several VS solutions, but there are several other reasons. The most common one is certainly the need to separate VS test projects from VS application projects.
.NET developers typically work with several VS instances opened at the same time. Developing Application-Wide means being able to jump from one VS instance to another. Thus, developing Application-Wide means making several VS instances smartly collaborate. Let's elaborate on this feature proposed since NDepend v4.
Concretely the NDepend team is facing the multi VS instances paradigm as anybody else with a sufficiently large code base. Not counting the test solution, the bulk of the NDepend code is spawned on 2 Visual Studio solutions: NDepend.Framework.sln and NDepend.UI.sln. These solutions are made of around 50K logical Lines of Code each. Projects from NDepend.UI.sln depend on projects in NDepend.Framework.sln.
To make Application-Wide a reality, a NDepend project must reference all assemblies built by all VS solutions (test code assemblies can be included or not, here we chose not).
This NDepend project will constitute the common base that will make several VS instances aware of each other. The same NDepend project must be attached to each VS solution.
Now several VS instances are ready to collaborate. For example if one ask for types that are using the namespace NDepend.Helpers…
…one got some types from several assemblies, including NDepend.UI. We can now choose to jump to a type source code declaration by double clicking it in the result. The declaration will be then opened, in the right VS instance, actually, the one with the NDepend.UI.sln solution opened. If NDepend.UI.sln was not currently opened, then the type declaration would be opened in the current VS instance.
The VS instances collaboration can happen from CQLinq query generated from Solution Explorer, from Solution Navigator, but also, from source code editor directly.
Application-Wide embraces also third-party code
Working Application-Wide means also caring for third-party code declared in referenced assemblies. Controlling third-party code used by your code is pretty important. At production time, third-party code is loaded and executed the same way as your own code. From the user perspective, there is no distinction between a problem coming from third-party code and a problem coming from your code. In the NDepend Class Browser panel, third-party code is blue. Since the initial NDepend project embraces several VS solutions, querying tier code will provide a list of code elements browsable across several VS instances.
Under the hood
Certainly, most software developer used to be a kid that destroyed his toys to see what was the magic inside. So let’s look under the hood about how the things work.
- Primary inputs analyzed by NDepend are assemblies themselves, the ones referenced by the NDepend project, the NDepend project which is common to all VS solutions. (For a more detailed discussion on NDepend inputs let's have a look at: NDepend Analysis Inputs).
- From assemblies NDepend tries to find corresponding PDB files. A PDB file represents an association between IL code in assemblies and source code declarations in textual source files. PDB files are used primarily by debuggers.
- From PDB files NDepend tries to find source files.
- From source files NDepend tries to find Visual Studio project files.
- From Visual Studio project files NDepend tries to complete the entire set of source files (because not all source files are referenced by PDB files, for example if a file contains only an interface declaration it won't be referenced!).
- From Visual Studio project files NDepend tries to find Visual Studio solution files.
- And that’s it!
Thus, when the NDepend VS addin is about to open a source file declaration of a code element, it actually knows which VS solution owns the code element. If the VS solutions is actually loaded in a VS instance on the machine, NDepend will choose this VS instance. The VS instances collaboration also works across VS versions (an opening source file request from a VS 2008 instance can actually open a declaration in a VS 2010 instance for instance).
We developed several tricky heuristics to recompose all the exposed file structure from assemblies files themselves. The result is that, when attaching a common NDepend project file to several Visual Studio solutions, all living Visual Studio instances becomes aware of each other and can collaborate. Several VS instance collaboration is a great feature. But another interesting point in embracing application-wide code through a single shared NDepend project file, is that the NDepend static analysis results and tools (CQLinq rules, dependency matrix/graph, 2 snapshots comparison, code coverage results...) also work application-wide. As a consequence, you can harness NDepend static analysis results and tools on the entire application, in each living VS instance. Concretely you can be informed on a flaw in VS sln B, from within a VS instance with VS sln A opened. Finally, let's notice that the heuristic can be somewhat blurred if some VS project are shared amongst several VS solutions. In that case several solutions can own a same code element. The heuristic will certainly find a VS solution but at a point in time, this might not be the VS solution you expected when trying to make several VS instances collaborate.