Editorial Note: In a previous article, Daniel wrote about Code Quality in the .Net ecosystem, where he briefly discussed Code quality tools included in Visual Studio and other third-party commercial tools like Resharper, JustCode, SonarQube, NDepend etc. This article discusses NDepend in detail.
Please note that this article is not a review nor a recommendation, rather it is only to provide information about different 3rd party tools in the market. We will be discussing many more 3rd party Code Quality tools in the near future to highlight some prominent features of these tools, as well as to help you decide if these tools are worth your time and budget.
Are you a .NET Developer who loves reading .NET magazines? Check out our free magazines at www.dotnetcurry.com/magazine
NDepend is a capable tool that effortlessly provides you loads of data and useful insights about your project. This data is also used by NDepend to define rules which are verified after successful builds and even used to estimate your technical debt.
Earlier this year, a new version of NDepend was released: NDepend 2017. This new release brings significant improvements over its previous versions, supports up to Visual Studio 2017 and finally includes support for .Net Core projects.
NDepend is also highly customizable, providing its own LINQ like syntax named CQLinq so you can easily customize or define your own queries and rules.
In this article, we will attach NDepend to an existing .Net Core project (The DockNetFiddle ASP.Net Core website built on a previous article) and experiment with some of its key features.
I hope you will find it interesting!
Attaching NDepend to the Project
The very first thing you need to do if you want to follow along is to clone the DockNetFiddle Github repo and switch to the CS2017-csproj branch. Then you will need to install NDepend, for which you can use a 14 days’ trial. Once NDepend its Visual Studio 2017 extension are installed, open the project with Visual Studio.
Attaching NDepend to the project is as simple as using the “Attach New NDepend project to current VS Solution” inside the new NDepend top level menu. You will be presented with a window where you need to select the assemblies which should be included in the NDepend analysis:
Figure 1, Attaching NDepend to the project
By default, this will show all the assemblies part of your solution. Select them all, leave “Build Report” selected and click on the Analyze button.
- Note, if you are still using the now deprecated project.json format and Visual Studio 2015, NDepend won’t be able to automatically detect your assemblies and you will manually need to select them, for example by recursively searching inside the folder
After the analysis is finished, you will be presented with a modal window aimed at first time users with direct links to some of the NDepend windows. Our first stop will be the dashboard so either use the direct access on that modal or just select NDepend -> Dashboard on the menu.
Once opened you will see a page full of useful information!
Figure 2, The NDepend dashboard
What does NDepend tell you about your code?
The first stop will naturally be the dashboard, where the following information is made immediately available to you:
- On the left-hand side, you see a summary of the size of your project. This includes figures like the number of classes and methods or lines of code. Be aware that single-line statements that you break into multiple lines for readability still count as a single line! This is particularly obvious with complex LINQ chains or fluent APIs like the one used in Startup.cs to configure the app and the ASP pipeline.
- In the middle, you will see the technical debt and coverage summaries. We will take a closer look at the technical debt section later but I won’t be getting into the code coverage features during this article. In case you are wondering, it supports code coverage data from NCover, DotCover and Visual Studio Code Coverage.
- On the right-hand side, you see a summary of the rules and quality gates results. It provides quick access to the number of rules/gates that failed and a breakdown of the rules per severity.
- Finally, scrolling down you will some graphs that will become more interesting as the analysis is repeated over time and you have a trend to display there.
- As you can see the dashboard provides in a glimpse a summary of the status of your project. And do you know the best thing about it? You can click and drill down into any of those results.
- For example, click on the rules violated. The Queries and Rules Edit window will open, displaying the CQLinq query used to calculate that figure (which you can modify) and its results below:
Figure 3, Drill down into the failed rules
You might have also noticed a new icon in the form of a colored circle on the right bottom side corner of VS. This provides a traffic-lights like indicator of your project depending on the rules/gates failed and the estimated technical debt. You can also move your mouse over it to see an expanded version of it in a floating window, almost like a mini-dashboard:
Figure 4, overall status displayed on the lower right corner of VS
The Code Metrics View
You can open this window by selecting NDepend -> Metrics -> Code Metrics View. It will open with a visual representation of your project using 3 settings:
- The level indicates the granularity of the View. This basically tells the view to draw a box for the selected level which can be any of field, method, type, namespace or assembly.
- The size indicates how big should be each box. For example, when Lines of Code are selected as the size and method is the selected level, you will see long methods as big boxes and short methods as small boxes.
- The color lets you select one of the key quality figures calculated for your code, providing a traffic-lights like indicator of that particular figure. You can also specify the boundaries between the green, yellow and red levels.
For example, I can select “methods” as the level, “lines of code” as the size and “methods called” as the color. I will then get to see the following diagram where yellow/red boxes are methods calling many other methods, the bigger the box, the longer the method:
Figure 5, the Code Metrics View
This is a powerful way of getting a better understanding of your code base, identify hotspots, areas for refactoring, etc.
Furthermore, when you hover on any of those items, a floating info window will appear providing the extended information for that item (and not just the size and color figure).
For example, when you hover over the Execute method in the diagram above, this window appears on the right side showing a full summary of the metrics calculated for that method:
Figure 6, the floating method info shown on hover in the Code Metrics window
Before we move on, I should highlight that you can click on any of the items to open that specific code file in Visual Studio. So, clicking on the Execute method in the diagram will open the ProgramExecutor class code file and scroll to the Execute method.
Understanding your code relationships and dependencies
NDepend provides with several useful ways of visualizing and navigating through your project dependencies, features built around the Dependency Graph and Dependency Matrix. These 2 windows present you with different visualizations of your project dependencies. The graph is a visual representation of this information while the matrix is a table with the raw number of usages between 2 given items.
The Dependency Graph
The Dependency Graph is probably the one you should take a look first. There you can alternate between a graph based on assembly dependencies or a graph between namespaces dependencies. You can also filter down to just your code or also include system/3rd party assemblies.
As with the Code Metrics view, there are a couple of settings which control how the elements in the graph are displayed:
- The box size
- The arrow thickness
For example, let’s see the dependencies between namespaces of the project using Lines of Code as the box size and members as the thickness:
Figure 7, The Dependency Graph window
If you are wondering about the box without a name, that’s the anonymous namespace! It is basically the one used for compiler generated types like when using anonymous types.
In this particular project, the RunController returns a json object by using an anonymous type as in return Json(new { result = … }), hence the dependency between the 2 namespaces!
Analyzing dependencies directly on the code window
One of the cool features in NDepend is that you can right click directly over the name of any type, method or namespace and find out who is using that particular item.
For example, if I right click a method I can see a few options to find out which types/methods/assemblies/namespaces are using that particular method either directly or indirectly:
Figure 8, finding out users of a particular method
Once one of those options is selected, a CQLinq query is generated and its results displayed. But that’s not the end of it. If you look careful at the results window, you will find there an option to export the results into the Dependency Graph!
Figure 9, exporting the method users into the Dependency Graph
Figure 10, the method users as seen in the exported graph
There is a small problem however. If you double check the previous screenshot you will notice I selected users of IProgramExecutor.Execute, that is, I found users of the method declared in the interface. Had I selected the users of the Execute method in the concrete class ProgramExecutor then I would had found no users!
This is because NDepend by default doesn’t follow interface implementations. This is where the CWLinq syntax for all its queries becomes useful, as we can use it to build a query that actually follows through interface implementations. This query has been taken from Stackoverflow as posted by Patrick from the NDepend team:
// Retrieve the target method by name
let methodTarget = Methods.WithFullName("DockNetFiddle.Services.ProgramExecutor.Execute(ProgramSpecification)").Single()
// Build an ICodeMetric< IMethod,ushort > representing the depth of indirect
// call of the target method.
let indirectCallDepth =
methodTarget.ToEnumerable()
.FillIterative(
methods => methods.SelectMany(
m => m.MethodsCallingMe.Union(m.OverriddensBase)))
from m in indirectCallDepth.DefinitionDomain
select new { m, callDepth = indirectCallDepth[m] }
Just enter that query in the Queries and Rules edit window instead of the default one you get when selecting types that are using me. And you can still export the results into the Dependency Graph:
Figure 11, the dependency graph for the modified query following interface implementations
The Dependency Matrix
In many projects, the number of assemblies and namespaces involved will be too big for a graph to be usable. You have the option of generating partial graphs using the export approach we have seen in the previous section.
An alternative approach would be using the dependency matrix found in NDepend -> Matrix -> View Dependency Matrix. It is similar to the Dependency Graph in the sense that you can select namespaces or assemblies, and restrict to just your own code.
Once opened you are presented with the following window:
Figure 12, the dependency matrix
There you can drill down to the specific element you are interested. For example, you can see the dependencies of a method by expanding its namespace, type and specific method rows.
If you are interested in a particular member, you also have the option of right clicking the name of a method/type in code and select View on Dependency Matrix.
It is definitely a soberer view than the Dependency Graph, but it will allow you to effectively work with large numbers of elements
The HTML report
A very interesting feature, especially for CI purposes, is that all the information we have seen so far is available as an HTML report. This includes visualizations like the Code Metrics View or Dependency Graph.
From within Visual Studio you just need to select the option “Run Analysis and Build Report” instead of the default “Run Analysis”. You can also change the NDepend settings to build the report every time the analysis is run, although as you can imagine that makes the analysis slower.
Once the report has been generated, you can open it with NDepend -> Report -> View Report. You could also directly open the generated files in project_root/NDependOut/NDependReport.html.
Figure 13, the HTML report
Running the analysis again after code changes
There are a few ways in which you can run again the analysis after you made some code changes. You have several different ways to manually run the analysis again:
- Use the shortcut Alt+F5
- Use the link on the dashboard. (Although on version 2017.1.1 this triggered F5, starting the project instead of running the analysis)
- Use the menu NDepend -> Analyze -> Run Analysis
- Use the link on the mini-summary that appears when hovering on the status icon at the bottom right corner
Even better, you can also configure NDepend to automatically run after a successful build.
Open the analysis settings, for example using NDepend -> Analyze -> Options to refresh analysis results in VS. There you can set the analysis to run after a successful build that isn’t intended for running the app or a test run.
Be aware of the option not more often than which is enabled by default! Ignoring that setting might lead to confusion as your builds won’t run the analysis unless enough time had passed since the last analysis.
Figure 14, analysis refresh settings in VS
That’s it, with the right settings enabled, the analysis will be run after each successful build.
Command line and CI Integration
So far, everything I have shown is based on the Visual Studio extension. But since one of the key NDepend features are the quality rules and gates which can be enforced, you might be wondering how can you integrate that in your CI pipeline.
The most basic option is by using the command line tool. If you open the installation folder you will notice a binary called NDepend.Console.exe. This tool basically allows you to run the analysis of a given project/solution from the command line, generating the report in the process and returning a non-zero code in case que quality gates fail.
You can check www.ndepend.com/docs/ndepend-console for a more detailed description of the options supported by the tool. In its most basic form you just point it to the .ndproj file created when you attached NDepend to you project/solution
· Specify the ndproj file using an absolute path like
>NDepend.Console.exe C:\Users\Dani\Documents\git\DockNetFiddle\DockNetFiddle.ndproj
Figure 15, NDepend.Console sample output
This tool could perfectly be the basis of the integration between NDepend and your CI server. For example, check this simple guide (www.ndepend.com/docs/jenkins-integration-ndepend) on the official documentation to use NDepend.Console with Jenkins.
Out of the box first class support is available for:
- TFS 2013
- Team City
- Cruise Control
Support for TFS 2017 and Visual Studio Team Services is under way and currently accessible ina beta state at www.ndepend.com/docs/vsts-integration-ndepend.
Exploring the Rules and Quality Gates
Let’s now take a look at one of the key features provided by NDepend. If you remember the dashboard, it shows a summary of the number of passed/failed rules and quality gates. Clicking any of those numbers opens the Queries and Rules Explorer window, which can also be accessed through NDepend -> Rules -> View Explorer Panel.
Figure 16, Queries and Rules Explorer window
This window shows all the rules configured by the project, divided into categories. All the categories are shown in the left-side panel, with the specific rules for that category shown on the right-side panel.
Categories or individual rules can be enabled/disabled using the checkbox.
Select for example the Immutability category which has some failed rules, one of them critical.
If you want to see the actual definition of the query and its results (ie, the members that caused the rule to fail) double click on it so the Queries and Rules Edit panel is opened.
Figure 17, queries and rules edit window
This window shows you the actual CQLinq query used as the rule definition and the members that matched it, considered as rule breaks.
- You can modify the CQLinq query and tweak any rule, you even get intellisense while doing so. Just save the rule whenever you are done and that’s it, you have customized your first rule.
- You can see unambiguously the criteria used for the rule. For example, check some of the rules in the Code Smells categories like Avoid methods with too many parameters and you will see methods with 7 or more parameters would break the rule.
If you are curious and check a few rules, you might realize that metrics like Lines of Code, Cyclomatic Complexity, IL Instructions, No of Parameters, etc are used in many of those rules. Since these will become so important, you might need to check what values are calculated for a particular member.
This is as easy as right clicking the name of the type/member and selecting NDepend -> Pin Description in Info View.
Warning: don’t close that window without unpinning the contents. Otherwise the next time it is opened, for example by selecting items in the Dependency Graph, the window will still be pinned to that particular type/method, causing a great deal of confusion.
Figure 18, the method info view pinned to a particular method
Quality gates
The Quality Gates are just a special category of rules that are considered build failures, thus making NDepend.Console to return a non-zero code and any CI server plugins to fail.
What makes them particularly interesting is that they can take into account rule results and trends, so it is possible to define quality gates like:
- No critical rule failures
- No more than a certain amount of technical debt
- No more than a certain percentage of technical debt increase since the last analysis
As with the Rules, they are based on the CQLinq syntax and you can see the actual definition by double clicking a quality gate and inspecting the query in the Queries and Rules Edit window. You could then easily customize them or use them as examples to define your own gates.
Creating your own rules
I have already said a few times that the CQLinq syntax for defining the Rules and Quality Gates is great because you can easily modify them or create your own. I think it is time to show an example of creating your own rule.
Let’s say that your team has agreed marker interfaces should be preferred over marker attributes:
- A marker interface is an interface with no members, whose intention is to act as metadata, tagging the member for the purposes of your application. It doesn’t provide any behavior, it just tags that member as of a special category.
- For example, you when following DDD and you might have decided that you want your domain classes to implement an empty IAggregateRoot marker interface. This way you know it is ok for that object to be referenced outside its aggregate as opposed to non-root objects which should only be referenced by other objects part of the same aggregate root.
- There is an interesting discussion regarding API design and performance in places like Stackoverflow about whether you should use marker interfaces or attributes!
In the default set of NDepend rules you will find a rule inside the Object Oriented Design which basically enforces not having marker interfaces. Since our hypothetical team has decided they prefer marker interfaces, the first step would be disabling that rule. Just unselect the Active checkbox.
The next step would be creating a similar rule, which will check for classes inheriting from Attribute that have no members. Let’s call this rule: Avoid marker attributes.
- In the Queries and Rules Explorer, select the Object Oriented Design category and click Create Query.
- The empty rule definition will open in the Queries and Rules Edit. Enter the name of the rule in the XML comment.
Figure 19, creating the new rule
Now we need to write the CQLinq query for it. Basically, we need to find classes that inherit directly from System.Attibute and contain no members (i.e. no methods, fields or properties).
We can start with the similar rule avoid empty interfaces as the starting point. Then modify it with the help of intellisense and the other rules as inspiration. In the end, you will come up with something similar to the CQLinq query I wrote:
// < Name >Avoid marker attributes
warnif count > 0 from t in JustMyCode.Types where
// Is an attribute class
t.IsAttributeClass &&
// Directly extends System.Attribute
t.DepthOfDeriveFrom("System.Attribute".AllowNoMatch()) == 1 &&
// Has no fields and no methods
// - Constructor counts as one method
// - Properties getter and setters are counted as methods
t.NbFields == 0 &&
t.NbMethods == 1
select new {
t,
t.TypesUsingMe,
Debt = (10 + 3*t.TypesUsingMe.Count()).ToMinutes().ToDebt(),
Severity = t.TypesUsingMe.Any() ? Severity.Minor : Severity.Info
}
Now that we have our own rule defined, we should put it to test. Let’s add the following code to the application and run the analysis again:
public class MyMarkerAttribute: Attribute
{
}
public class MyAttribute: Attribute
{
public string SomeProperty { get; set; }
}
public class MyInheritedAttribute: MyAttribute
{
}
[MyMarker]
public class EmptyClass
{
You should see that only the MyMarkerAttribute was detected as a rule break. This is exactly what we expect as the other attribute classes are fine because they either have members or are no direct descendants of Attribute.
Figure 20, the avoid marker attributes rule in action
Disabling rules
Even this hypothetical team who tries to avoid marker attributes might find valid use cases for them. However, there is no such thing as disabling a rule for a specific member. In NDepend you can only disable rules globally.
Ironically, the fact that you can customize and define your own rules using CQLinq means that you can use a marker attribute and update your rule to ignore results with that attribute!
So basically, we can create a marker attribute to allow disabling the rule avoid marker attributes on specific classes. Hilarious.
Add the following classes to your code:
public class NDependDisableAttribute : Attribute
{
public string Name { get; set; }
}
public class NDependDisableAvoidMarkerInterfaceAttribute: NDependDisableAttribute
{
public NDependDisableAvoidMarkerInterfaceAttribute()
{
Name = "Avoid marker attributes";
}
}
Now update the rule definition with one last criteria, specifying the method should NOT contain the newly created attribute:
// Rule has not been manually disabled for this particular class
&& !t.HasAttribute(
"DockNetFiddle.Models.NDependDisableAvoidMarkerInterfaceAttribute".AllowNoMatch())
Then run the analysis adding/removing the attribute to MyMarkerAttribute and notice how the rule breaks or not:
// This will be found as rule break or not depending on whether the disable attribute is added/removed
[NDependDisableAvoidMarkerInterface]
public class MyMarkerAttribute: Attribute
{
}
Keeping the technical debt under control
When you cloned DockNetFiddle from Github and run NDepend for the first time, you might have noticed in the dashboard a few rules break including a critical rule which then caused the quality gates to fail.
Right now, our project status as indicated by the icon on the bottom right corner of Visual Studio is red. We have gates broken due to critical rule failures. We also have an estimation of the technical debt of our project, both in terms of man-days and relative percentage compared to the effort it took us to write the current code in the first place.
Figure 21, initial status of the project
If you click on the broken critical rules, you will find I had violated the avoid non-readonly static fields in the ProgramSpecification class.
The technical debt incurred by every failure of this rule can be seen in the rule definition. Take a look at the select part of the query and notice how the Debt field is calculated:
select new {
f,
methodAssigningField,
Debt = (2+8*methodAssigningField.Count()).ToMinutes().ToDebt(),
Severity = Severity.Major
}
- This means the technical debt, in terms of time needed to fix this issue, it is calculated as 2 minutes plus 8 extra minutes for every method that assigned a value to this field. Since no one is assigning a value on any of those 3 fields, in this case the tech debt to fix all of them would be 6 minutes.
- Being part of the CQLinq query, you can customize the way the technical debt is calculated for any rule, and use it to provide an estimate on any new rules you create.
- You can play around with the Tech Debt NDepend settings which define how the debt is converted into man-days and money.
Ok, so let’s just add the readonly modifier to the static fields of that class, DefaultProgram, DefaultProject and Default. Build and run the analysis again, you should now see better results and interestingly you will also see the improvements you made since the previous analysis:
Figure 22, fixing NDepend issues
That’s nice, and we also got rid of the gates failure since we have fixed the critical rules. However, notice how there is also a new rule break, that info rule has crept in!
- If you take a look, it is basically a rule saying I shouldn’t declare a static readonly ProgramSpecification property since ProgramSpecification is a class and anyone can change its inner public properties.
- Okay, let’s refactor that class, converting DefaultProgram and DefaultProject into private properties and replacing the Default instance with a static GetDefaultProgram method.
Compile and run the analysis. You will be surprised to see worse results and a critical rule broken: API breaking changes: Fields. Since we have removed a public field NDepend complains that we should be good citizens and let anyone that could have been using this field now that it’s deprecated.
- This is great when you are writing a library that will be consumed by 3rd party code.
- However, NDepend doesn’t know this isn’t the case on this project and any possible users of this class would be contained within the solution.
- At this point you might want to either disable the rule or at least customize it so it only applies to certain namespaces you consider your interface with the world outside.
In either case, you should see bigger improvements:
Figure 23, further improvements to the project
There are still minor rules broken, some of which you could fix in your code and some others would require rules to be customized so NDepend knows about the particularities of the framework you are dealing with.
For example, NDepend thinks MVC controllers should be made static since no one is instantiating them. You could tweak the rule so this doesn’t apply to public classes (which would be required if you are writing a library) or be more precise and for example ignore failures of this rule for classes ending with the Controller suffix.
One of the coolest things about the technical debt is that in your quality gates you can define rules based on the technical debt and its evolution, so you can fail if a namespace has too much technical debt or if the increase since the previous analysis/baseline is bigger than a certain percentage.
All of which are interesting use cases for your CI pipeline!
Conclusion
Personally, I have found NDepend very interesting. The insights you can get about your project and the different ways you can visualize them are very powerful. The way it then ties those insights and metrics about your code into fully customizable rules and quality gates can be really interesting on some projects and/or teams. Projects and teams who would then benefit by the trends and technical debt figures provided by NDepend.
Is it perfect? By no means. The amount of information presented to the user is huge and the way this information is presented and navigated through isn’t always intuitive. I have also found the default layout hard to use especially with elements like the Queries and Rules Edit which open within the solution explorer by default. Finally, the interface is heavily based around mouse controls and could allow deeper customization especially regarding adding/defining keyboard shortcuts, which might be a deal breaker for some.
I would agree most of the issues are mostly a matter of getting used to the tool and how it works. After some time using NDepend you get used to it and won’t notice them, but they are there and should be taken into account. After all, it’s the full team who will use and benefit from the tool, so the easier it is to adopt and embrace it the better.
But is it worth it? That it’s entirely up to you to decide. NDepend is useful in many situations but ultimately your project size, budget, complexity and team that would determine whether you actually need it or not. Maybe NDepend is the right tool for you or maybe StyleCop and Resharper is all you need.
Thanks to Patrick Smacchia from the NDepend team who allowed me to try NDepend!
This article has been editorially reviewed by Suprotim Agarwal.
C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.
We at DotNetCurry are very excited to announce The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).
Organized around concepts, this Book aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on the latest .NET Core 3.0, .NET Standard and C# 8.0 (final release) too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.
Click here to Explore the Table of Contents or Download Sample Chapters!
Was this article worth reading? Share it with fellow developers too. Thanks!
Daniel Jimenez Garciais a passionate software developer with 10+ years of experience who likes to share his knowledge and has been publishing articles since 2016. He started his career as a Microsoft developer focused mainly on .NET, C# and SQL Server. In the latter half of his career he worked on a broader set of technologies and platforms with a special interest for .NET Core, Node.js, Vue, Python, Docker and Kubernetes. You can
check out his repos.