Recently, Scott Hanselman
has proposed a survey to know which part of the .NET Framework real-world developers are
The result will be interesting mostly for Microsoft engineers in charge of the .NET
Framework. However, it might be interesting for a team to know which tier code
its code base really uses. You
certainly know coarsely if your code base is using or not, libraries such as
ADO.NET, WPF, Windows Form, ASP.NET but also NHibernate, NUnit or Log4Net. But can you answer these 2 questions?
part of the code base uses which library?
types and members of the library are really
used by your code base?
Before rambling on why you should care about these questions and what are the
perspectives offered by an accurate control of the usage of tier code, let’s illustrate how NDepend can help answering such questions and more in seconds.
Let’s analyze the code of the OSS (yet very professional) application
Paint.NET v3.0. Assemblies
of the application are in black while library/tier/framework assemblies are in
blue. The Dependency Matrix shows which assemblies of the application are using
which tier assemblies. For example, we can see that the library SharpZipLib
is only used by the assembly Paint.NET.
Things get really interesting
when we unfold tier assemblies. Indeed, during analyzes, NDepend only focus on
code elements of tier assemblies that are really used by the application. Here
the Dependency Matrix unfold possibility is a powerful tool to dig in who is
using what. For example, the following screenshot shows that 8 members of the
class List<T> are used by 7
methods of the assembly Paint.DotNet.Data.
One can easily browse
this coupling with the Dependency Matrix…
…but also, with the Dependency
Graph which is clearly more appropriate here:
Diff in the usage of tier code
NDepend allows comparing
2 different builds of an application,
to know which code has been refactored, added or removed. While a code base is
evolving, the way it uses libraries is evolving also. Some libraries are
discarded, some tier classes that wasn’t used are now used… While comparing 2
builds, NDepend also compare the tier code usage. Hence, while unfolding a
library code in the class browser, we can easily pinpoint which tier code
elements that weren’t used are now used (in bold), which tier code elements are
not used anymore (striked) and which tier assemblies/namespaces/classes contains
some usage change (underlined). The following screenshot illustrate the usage
diff of collections between Paint.NET v2.72 and Paint.NET v3.0.
We can see at a glance
for example that between these 2 releases, developers of Paint.Net thought that
using the interface IDictionary<TKey,TValue>
was a good idea.
If you prefer, you can harness the 3 dedicated Code Query
conditions IsUsedRecently / IsNotUsedAnymore / IsUsedDifferently to query the diff in the usage of tier code:
SELECT TYPES FROM NAMESPACES “System.Collections.Generic” WHERE IsUsedRecently / IsNotUsedAnymore / IsUsedDifferently
Constraining the usage of tier code
While this is cool to
control the usage of tier code with such accuracy, I would like to explain how this
possibility is useful for developers.
One of the direct
application is to forbid some incorrect usage of a library. For example, as explained
while running with .NET v2.0 runtime, the ultra-popular method String.IsNullOrEmpty(String) can raise a
NullReferenceException. I don’t
really know if this bug has been fixed but in fact I don’t care, because we
wrote the following CQL rule to make sure that we don’t use this method (we use
our own implementation actually):
// <Name>String.IsNullOrEmpty() is
bugged and must not be used</Name>
WARN IF Count > 0 IN SELECT METHODS
WHERE IsDirectlyUsing “OPTIONAL:System.String.IsNullOrEmpty(String)”
Notice the OPTIONAL:
prefix. Without it, this query wouldn’t compile. Indeed, as the method IsNullOrEmpty() is not used by our code
base, the analysis doesn’t reference it. Without the OPTIONAL: prefix the CQL
compiler would emit a ‘Cannot find method named …’ error.
Let’s dig in another real-world example. We are relying on the
DevExpress DXperience Windows Form
library to enhance the usability of our product. Amongst plenty of cool stuff,
this library lets control the skinning of controls.
This is only possible if
you use the DXperience buttons (and other controls) instead of the classical
Windows Form button. To prevent a developer to use a classical Windows Form
button, and then to guarantee a coherent skinning of our UI, we wrote the following
// <Name>All buttons should be of type
DevExpress.XtraEditors.SimpleButton and not System.Windows.Forms.Button</Name>
WARN IF Count > 0 IN SELECT FIELDS FROM ASSEMBLIES WHERE
The possibilities to
control how your code uses libraries are endless. We provided by default a set
of around 30 rules (some borrowed from FxCop, some others from our experience)
about the usage of the .NET Framework.
We expect to release more default rules in the future, also relative to popular
framework usage such as NHibernate or
Log4Net. What is really cool is that each time a developer detects a particular library usage issue, he can very easily define some custom rules about it. The rules can be named appropriately and contain further comment detailing the issue. This way its co-worker will be aware of this issue and won’t redo the same error in the future. Hence such set CQL rules helps capitalizing on the experience acquired the hard-way on a library. Btw, don’t
hesitate to report us the coolest rules you found on any popular library (.NET framework included).
Separation of Concerns and usage of tier code
I would like to mention
that a few months ago, I explored in this blog post, Dependencies and Concerns,
an interesting application of controlling the usage of tier code. Basically the
idea is, tell me what you are using and
I can tell you with what you are concerned about. Hence, the post explains how
you can write some rules, to forbid for example using some ADO.NET stuff at the
same time as using UI stuff. In other words, you get a mean to write rules to
check that concerns of you application are properly separated (like, the UI
code shouldn’t use directly the DB code).
Another interesting field to explore is to make sure that special concerns such as threading, error handling,
logging, users’ authentication, security permissions… are strictly used within some bounded regions of your code.
WARN IF Count > 0 IN SELECT METHODS OUT OF NAMESPACES
“Product.UI.ThreadingHelper”, “Product.DB.ThreadingHelper” WHERE