What ? Application-Wide ?
When an application reaches a certain amount of code, the application
code gets usually partitioned amongst several Visual Studio solutions.
Visual Studio hosting some popular addins like Resharper or CodeRush end
up slowing down. 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 3000US$ laptop (32bits dual core + SSD + 8GB RAM + Wnd7) I
experiment 40 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 I consider it as a major
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. I am going to elaborate on this feature
proposed by NDepend v3.
Concretely the NDepend team is facing the multi VS instances paradigm
as anybody else with a sufficient large code base. Not counting the
test solution, the bulk of the NDepend code is spawned on 2 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 choosed not).
This NDepend project will constitute the common
base that will make several VS instances aware of each others. The same
NDepend project must be attached to each VS solution.
Now several VS instances are ready to
collaborate. For example if I ask for types that are using the namespace
…I got some types from several assemblies,
including NDepend.UI. I 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
CQL query generated from Solution Explorer but also, from source
code editor directly.
Application-Wide embraces also tier code
Working Application-Wide means also caring for tier code declared in
referenced assemblies. Controlling tier code used by your code is pretty
important. At production time, tier 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 tier code and a problem coming
from your code.
In the NDepend Class Browser panel, tier 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
I guess that 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 you can look at this post: NDepend
- From assemblies NDepend tries to find corresponding PDB files. A PDB
file represent an association between IL code in assemblies and source
code declarations in textual source files. PDB files are used primarily
- 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
- 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 own
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 cool. 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
(CQL 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.