If you're a developer, you're probably familiar with various tenets of your craft, such as "naming things is hard" and "every non trivial program has at least one bug". The latter example is one of the reasons why there are ever increasing amounts of tools designed to reduce the number of bugs in an application, from testing, to performance profiling, to code analysis.

In this article, I'm going to briefly take a look NDepend, a code analysis tool for Visual Studio. This is the point where I'd like to quote the summary of the product from the NDepend website, but there's no simple description - which sums up NDepend pretty well actually. This is a complicated product offering a lot of features.

So when I say "a brief look", that's exactly what I mean. When I've had a chance to explore the functionality fully I hope I'll have enough knowledge and material to expand upon this initial post.

Disclaimer: I received a professional license for NDepend on the condition I would write about my experiences.

What is NDepend and what can it do for me?

Simply put, NDepend will analyse your code and spit out a report full of metrics, and violations against a large database of rules. These might be the mundane (a method has too many lines) to the more serious (your method is so complicated you will never remember how it works in 6 months time).

This really doesn't even begin to cover it though, as it can do so much more, from dependency graphs to trend analysis. One of the interesting things about NDepend is it saves the results of each analysis you do, allowing you to see if metrics such as test coverage are improving (good) or critical violations increased (not so good!).

A sample project

For this article, I'm going to be using the Dithering project I created in previous blog posts to test some of the functionality of NDepend. I choose this because the project was fresh in my mind as I've been heavily working on it the last few weeks, and because it was small enough that I assumed NDepend wouldn't find much amiss. Here's another tenet - assumptions are the mother of all <censored>.

You can use NDepend one of two ways, either via a stand alone application, or via a Visual Studio extension. For this article, I'm going to be using Visual Studio, but you should be able to do everything in the stand alone tool as well. There's also a CLI tool which I assume is for build integration but I haven't looked at it yet.

That first analysis

If this is the first time using NDepend, you need to attach an NDepend project to your solution.

  • Open the NDepend menu and select the Attach New NDepend Project to Current VS Solution menu item
  • The dialog that is displayed will list all the projects in your solution, if there any you don't want to include in the analysis, just right click them and choose the appropriate option
  • Click the Analyze button to generate the project
  • Once the project has been created, a welcome dialog will be displayed. Click the View NDepend Dashboard button to continue

This will open the dashboard, looking something similar to the below.

A HTML report will also be generated and opened in your default browser, providing a helpful synopsis of the analysis.

The initial dashboard for the Dithering project
The initial dashboard for the Dithering project

At this point, all the charts you can see are going to be non-existent as you have to rerun the analysis at future times in order to get additional data points for plotting.

The main information I'm interested in right now is contained in the Code Rules block. And it doesn't make me happy to read it:

  • 4 Critical Rules Violated for a total of 9 Critical Violations
  • 37 Rules Violated for a total of 215 Violations

Wow, that's a lot of violations for such a small project! Lets take a look at these in detail.

Viewing Rules

Clicking the blue hyper-links in the Dashboard will automatically open a new view to drill down into the details of the analysis. On clicking the Critical Rules Violated link, I'm presented with the following

Viewing rule violations
Viewing rule violations

Clicking one of the rules in the list displays the code of the rule and the execution results.

Viewing the results of a violated rule
Viewing the results of a violated rule

Here we can see the the violation is triggered if any method has more than eight parameters. In the dithering example project, there is a class I that I used to generate the diagrams used on the blog posts, and the DrawString method of this helper class has 10 parameters, thus falling foul of the rule. Great start!

The next rule on the list is a bit more complicated, but essentially it's trying to detect dead code. In a non-library project, this should be fairly straight forward and true to form it has detected that the ArticleDiagrams class and its methods are dead code.

A more complicated rule with a lot of conditions
A more complicated rule with a lot of conditions

This is actually a very useful rule if your coding standards insist that all dead code is removed. How useful depends on your code coverage, if you also have a 100% rule then you should already found and removed such code.

So far so good. Lets look at the final critical rule failure.

When rules go wrong

The last critical rule violation is Don't call your method Dispose. I imagine this makes a lot of sense, if your class doesn't implement IDisposable, then having a method named Dispose is going to be confusing at best.

I'm either mad, or this is a false positive
I'm either mad, or this is a false positive

Interesting. So it somehow thinks that the MainForm and AboutDialog classes - both of which inherit from Form - shouldn't have methods named Dispose. Well, somewhere in its inheritance chain Form does implement IDisposable so this violation is completely wrong.

As a test, I added IDisposable to the signature of AboutDialog and re-ran the NDepend analysis. It promptly decided that the Dispose method in that class was now fine. Of course, now Resharper is complaining Base interface 'IDisposable' is redundant because Cyotek.DitheringTest.AboutDialog inherits 'Form'. Sorry NDepend, you're definitely wrong in this instance.

At this point, I excluded the ArticleDiagrams class from the solution and reran the analysis, removing some the violations that were valid, but not really appropriate as it was dead code.

More violations

So far, I've looked at 4 failed rules. 3 I'm happy to accept, and if this were production code I'd be getting rid of the dead code and resolving all three. The fourth violation is flat out wrong and I'm ignoring it for now.

However, there were lots of other (non-critical) violations, so I'll have a look at those now. The Queries and Rules Explorer window opened earlier has a drop down list which I can use to filter the results, so now I choose 31 Rules Violated to look at the other warnings.

A bunch of important, but not critical, rule violations
A bunch of important, but not critical, rule violations

There's plenty of other violations listed. I'll outline a tiny handful of them below.

Override equals and operator equals on value types / Structures should be immutable

This pair of failures is caused by the custom ArgbColor struct and is the simplest structure to handle a 32bit colour. Actually, this struct is being called out for a few rules all of which I agree with. If this were production code, I'd be following a lot of the recommendations it makes (in fact, in the "real" version of this class in my production libraries I do follow most of them - a key exception being my structs are still mutable).

Static fields should be prefixed with a 's_' / Instance fields should be prefixed with a 'm_'

These rules vie between I disagree with them, and NDepend shouldn't be picking them up. In the first place, I disagree with the rule - I simply use an underscore prefix and leave it at that.

However, NDepend is also picking up all of the control names in my forms. I seriously doubt any developer is going to use m_ in front of their control names and so I don't think NDepend should be looking at these - I consider them "designer" code of sorts and should be excluded. There's a few more rules being triggered by controls, and I think it's looking messier than it should.

I can edit the rule to use my own conversion of the plain underscore, but I can't do much about NDepend picking up WinForm control names.

Non-static classes should be instantiated or turned to static

This is an interesting one. It's basically being triggered by the LineDesigner class, a designer for the Line control to allow only horizontal resizing. Control designers can't be static and so this rule doesn't apply. It is referenced by the Designer attribute of the Line class so we probably just need to edit the rule to support it.

And more

There's quite a few rule violations so I won't cover them all. It's an interesting mix of rules I would find useful, and rules subject to interpretation (an example is if I have an internal class I still mark its members as public, NDepend think this is incorrect).

But, NDepend doesn't force you to accept its view. You can simply turn off any rule that you don't want influencing the analysis and it will be fully disabled, including the dashboard updating itself in real-time.

Assuming you have analysed the project multiple times, you can turn on recent violations only, thus hiding any previous violations. You may find this very useful if you are working from a legacy code base!

Editing Rules

With that said, there are other options if a rule doesn't quite fit the bill. NDepend uses LINQ with a set of custom extensions (Code Query over LINQ (CQLinq)) as the base of its rules. So you can put your programmer hat on and modify these rules to suit your needs.

As a concrete example, I'm going to look at the Instances size shouldn't be too big rule. This has flagged the Line control as being too big, something I found curious as the control is a simple affair that just draws a 3D line. When I look at the details for the violation it mentions 6 fields. But the control only has 3. Or does it?

Why does this rule think a class with 3 fields really has 6?
Why does this rule think a class with 3 fields really has 6?

The query results don't include the names of the fields, so I'm going to adjust the code of the rule to include them. This is a really nice aspect of NDepend - as I type in the code pane, it continually tries to compile and run the rule, including syntax highlighting of errors, and intellisense.

I added the , names = ... condition to the code as follows, which allowed me to influence the output to include an extra column

csharp
warnif count > 0 from t in JustMyCode.Types where 
  t.SizeOfInst > 64 
  orderby t.SizeOfInst descending
select new { t, t.SizeOfInst, t.InstanceFields, names = string.Join(", ", t.InstanceFields.Select(f => f.Name)) }
Apparently because an event is a field!
Apparently because an event is a field!

The results of the modified rule show that there are 3 variables which are backing fields for properties, and then 3 events. Is an event a field? I don't think so, an event is an event. But NDepend thinks it is a field. Regardless though, by editing the rule I was easily able to add additional output from the rule, and although not demonstrated here I've also used some of the built in filtering options to exclude results from being returned.

The ability to write your own rules could potentially be very useful with many possibilities.

Interpretation is king

In a way, I'm glad that NDepend doesn't have the ability to automatically fix violations the way some other tools do. I ran NDepend on my CircularBuffer library, and one of the suggestions was to change the visibility of the class from public to internal. Making the single class of a library project inaccessible to consumers isn't the best of ideas!

I think what I'm leading to here, is use common sense with the violations, do not just blindly accept anything it says as gospel.

Viewing Dependencies

Any application is going to have dependencies, and depending on how tight your coupling is, this could be an evil nightmare. You can display a visual hierarchy of the dependencies of your project via a handy Dependency Diagram - below is the one for the dithering project. Quite small as there are few references, The thicker the arrow, the more dependencies from the destination assembly you're using.

Easy dependency viewing
Easy dependency viewing

In the case where the diagram is so big as to become meaningless, you can also view a Dependency Matrix - this lets you plot assemblies against each other and see the usages.

Viewing code dependencies via a matrix
Viewing code dependencies via a matrix

Clicking one of the nodes in the matrix will then open a simplified Dependency Graph, making it a little easier to browse than a huge spaghetti diagram.

Code Metrics

Many years ago, I used a small tool that displayed the size of the different directories on my computer in a treemap to see which folders took up the most space. I haven't used that tool for years (I don't need a colour graph to know my Steam directory is huge!) but I do find that sort of display to be oddly compelling.

NDepend makes use of a tree map to display code metrics - the size of the squares defaults to the code size (useful for seeing huge methods, although again, as the screenshot below indicates, I really wish NDepend would exclude designer code). You can also control the colour of the square via another metric - the default being complexity, so the greener the square the easier the code should be to maintain.

An easy way to gauge the health of your code
An easy way to gauge the health of your code

I couldn't see how to access this from Visual Studio, but the HTML report also includes an Abstractness versus Instability diagram which "helps to detect which assemblies are potentially painful to maintain (i.e concrete and stable) and which assemblies are potentially useless (i.e abstract and instable)". Meaning you should probably take note if anything appears in the red zone!

NDepend doesn't think WebCopy's code is unstable. Well, at least that's one thing that isn't
NDepend doesn't think WebCopy's code is unstable. Well, at least that's one thing that isn't

Updating the analysis

You can trigger a manual refresh of the analysis at any time, but also by default NDepend will perform one after each build, meaning you can always be up to date on the metrics of your project.

Show me a big project

So far I have looked at only a small demonstration project. However, as the ultimate test of my review, I decided to scan WebCopy. I was very curious to see how NDepend would handle that solution. NDepend scanned the code base quite nicely (despite an old version of one of my libraries getting detected and playing havoc)

As an indication of the size of the project, it reports that WebCopy has 60 thousand lines of code (translating to half a million IL instructions), 24 thousand lines of comments, and nearly 1800 types spread over 44 assemblies. A fair amount!

I had a quick look through the violations list, and noticed a few oddities - there are lots of Forms in these projects, yet the Don't call your method Dispose violation that so annoyed me earlier was only recorded 4 times. One of these was actually valid (a manager class who's children were disposable), while the others weren't. Still, there's a curious disparity in the way NDepend is running these rules it seems.

I did find some violations indicating genuine problems (or potential problems) in the code through so at some point (sigh - there's a lot of them) I will have to take a closer look and go through them all in detail.

Just before I sign off, I shall show you the dependency diagram (maybe I need to try and make my code simpler!) and the complexity diagram.

You are looking at a window into Code Hell. Fear it
You are looking at a window into Code Hell. Fear it
A bit too much red here for my liking
A bit too much red here for my liking

That's all, folks

For a "brief" overview, this has been quite a long article - NDepend is such a big product, one article cannot possibly cover it all. Just take a look at their feature list!

Ideally I will try to cover more of NDepend in future articles, as I'm still exploring the feature set, so stay tuned.

Update History

  • 2015-06-27 - First published
  • 2020-11-21 - Updated formatting

Like what you're reading? Perhaps you like to buy us a coffee?

Donate via Buy Me a Coffee

Donate via PayPal


Comments