NDepend Review and Tips

Posted by on in Blogs
On and off for the past few months, I've been experimenting with the .NET static analysis tool NDepend. NDepend works somewhat differently than most other static analysis tools I've tried. Notably,

  • Most tools tend to work on either compiled assemblies or source code. NDepend uses both.

  • Most tools implement their tests in compiled, executable code. NDepend, on the other hand, uses a proprietary, domain-specific language called CQL.

  • NDepend can enhance its analysis by importing analysis data from related builds, like NCover, Team System coverage analysis, and can compare multiple builds of the same project.


As with other static analysis tools, however, NDepend is designed to be used both within Visual Studio and as part of an automated build/continuous integration process.

NDepend claims to support "any .NET language including C#, VB.NET, MC++ and C++\CLI." To put this claim to the test, I ran it against a Delphi Prism application. Indeed, it did produce useful results in the analysis, although there were some quirks. It couldn't find the assembly from the solution, but after I located it manually analysis could run. I suspect NDepend doesn't parse the source code of Prism applications as it does with C#.

What NDepend Does


The name "NDepend" presumably comes from the dependency analysis feature. NDepend will display dependencies between assemblies either as a cross tab or a directed graph. See the website for illustrations of this feature.

NDepend also does static analysis along the same lines as Microsoft FxCop and StyleCop, only in a much more interactive manner. By combining the features I've already mentioned, you can easily ask queries like, "What are the most complex methods I've changed recently which have no unit test coverage?"

All of NDepend's features are available in its own GUI application, from the command line, within Visual Studio, and in a custom MS build task. So, as with most static analysis tools, you can integrate NDepend into a continuous build environment in order to set enforceable standards on code complexity and the like.

Installation


NDepend does not have an installer. You need to create a folder for the application, and unzip an archive file into that folder. To install the integration with Visual Studio, you click a button in the GUI application, once it's running. I find point and click installers a bit more convenient, but it was certainly easy to get the application working.

Default Analysis of a Do-Nothing Application


NDepend is quite strict even in its default configuration. For example, I ran it on a do-nothing, "File->New" ASP.NET MVC 2 Preview 1 application, with no code of my own added. It reported quite a number of warnings.

Many of the warnings were incorrect, because NDepend did not automatically find the System.Web.Mvc assembly, which is not stored along with the rest of the .NET framework, even though it is in the GAC. In general, the current version of NDepend will only find assemblies which are in the solution folder or are in the .NET Framework folders automatically. You should add other folders for referenced assemblies manually, even if those assemblies are GACced. To fix this, I had to do the following steps:

  1. Go to the Project Properties tab in the GUI.

  2. Click the "View Folders" button.

  3. Click the "Browse" button and manually locate the folder containing the required assembly.


With these steps completed, the quality of the analysis improved quite a lot.

NDepend shows the results in its GUI and also produces an HTML report. The HTML report for the do-nothing application was 14 pages long, and the report for our production web application ran over 50 pages. This can be kind of overwhelming for a new user. The issues flagged in the do-nothing application included (with my comments in parentheses):

  • Methods with > 10 lines of code should be commented. (I disagree; methods should be commented when their intention cannot be made clear in code. This has little relationship to method length and is difficult to impossible to flag with static analysis.)

  • Types with no state should probably be static. (Worth considering.)

  • Types with no subtypes could be sealed. (NDepend makes it easy to turn this off for public types.)

  • Classes with small instance sizes, etc., could be structs. (Maybe, if you want value semantics.)

  • Avoid namespaces with few types. (This is expected on a File->New application; would work better in a "real" app. FxCop has the same rule.)

  • You should't have types outside a declared namespace. (I agree.)

  • Avoid [un] boxing. (I agree; great test!)

  • Types which "could be internal." (NDepend can't pick up instantiation via reflection, making this less useful in the specific case of MVC.)

  • Fields which are assigned only by the constructors of their types should be marked read-only. (I agree; this is a really useful test.)

  • Type names should begin with an uppercase letter. The default MVC application includes a type called "_Default".


For the sake of comparison, I analyzed the same application using FxCop. This produced 11 warnings, about half correct, and about half incorrect. Incorrect warnings included not recognizing the TLA MVC, asserting that Application_Start should be static, and claiming that the application should target .NET Framework 3.5 SP 1 (as opposed to just 3.5), something which Visual Studio 2008 does not allow. Probably correct warnings included that the application should have the CLSCompliant attribute, and that the _Default type was oddly named.

Customizing the Analysis For a Real Application


NDepend's use of its CQL language to implement its test makes individual test very easy to customize. For example, our production web application includes the Microsoft DynamicQuery library, which is distributed as a single CS file. Because we included this in one of our assemblies instead of creating a separate assembly for it, NDepend analyzes it along with the rest of our assembly. It contains many long methods, and other things which NDepend does not like, but because this is Microsoft's code, and not ours, I would like exclude it from the analysis. So I can, for example, change the test for "method too long" from:

// <Name>Methods too big (NbLinesOfCode)</Name>
WARN IF Count > 0 IN SELECT TOP 10 METHODS WHERE NbLinesOfCode > 30 ORDER BY NbLinesOfCode DESC
// METHODS WHERE NbLinesOfCode > 30 are extremely complex and
// should be split in smaller methods
// (except if they are automatically generated by a tool).
// See the definition of the NbLinesOfCode metric here http://www.ndepend.com/Metrics.aspx#NbLinesOfCode

to:
WARN IF Count > 0 IN SELECT TOP 10 METHODS
WHERE NbLinesOfCode > 30
AND !(FullNameLike "^System.Linq.Dynamic")
ORDER
 BY NbLinesOfCode DESC


What if you don't know how to write a regular expression? Well, then you need to learn how to write a regular expression. Regular expressions are the only option for this kind of name matching in NDepend.

It might seem at first that using a proprietary language would be a barrier to using the tool. After all, other tools, like FxCop can be customized using C# and other .NET languages. Doesn't a proprietary syntax like CQL discourage customization? Not really, in my experience. The real win of NDepend is that you can customize the rules interactively, within the GUI. In contrast to building a custom FxCop rule and restarting FxCop, this is a huge win, because it encourages the use of ad hoc queries and trial and error to build the ideal heuristic for your application. Coupled with auto complete in the CQL editor, it makes the relatively minor inconvenience of dealing with a proprietary language almost irrelevant. Yes, I would prefer to use LINQ, but the interactivity of CQL is a much bigger win.

However, you don't get the same kind of customizability at a global level. You can include and exclude assemblies from analysis globally, but that's about it. So while you can include and exclude code based on almost any imaginable criterion at the level of an individual test, I have not found any way to do an exclusion like the one I demonstrate above globally. (Note, however, that in a comment on this blog a week ago, a member of the NDepend team suggested that features along this line may be coming next year.)

I should add that most static analysis tools seem to work this way. They typically make it fairly easy to include and exclude entire tests or specific tests on specific cases, but don't typically include a way to say, "Don't run any test on any bit of code which meets the following criteria, ever." So NDepend is no worse than other tools I've used in this regard, and it's features to customize individual tests are considerably better than most other tools, although the learning curve is a bit steeper.

Like every static analysis tool I've ever tried, NDepend does not produce useful results for generated code. Since this is not an NDepend-specific issue, I discussed it in an earlier post, along with some tips for excluding generated code from NDepend's analysis.

Documentation


The documentation in the NDepend online help is a bit thin. But the authors have written a number of informative blog posts on how to use specific features and tips for getting the most out of the tool. Once you realize that these posts exist and know where to find them, it is quite a bit easier to read up on how to use the tool effectively.

Usability


I'm of two, contrasting opinions regarding the usability of NDepend. I found the initial learning curve to be quite steep, and elements of the user interface to be not very discoverable. On the other hand, once I figured out how to use the application, which took me a few weeks (maybe I'm just slow), I found the application to be very usable, and I could do what I needed to do very quickly. Watching the videos on the NDepend website helps quite a lot in getting over this initial hump. I've also had some e-mail discussions with the makers of the software on how they might improve some of the things this new user found initially confusing in their next release.

For those new to the application, here are some things which may help you get started:

  • The CQL query editor has auto complete, so it's fairly easy to customize a query even if you're unfamiliar with the language. However, if you intend to do any serious work in CQL, it is worth seeking out and spending some time reviewing the CQL examples on the NDepend website.

  • After you do an initial analysis, look at the error list before considering the analysis results. Adjust the configuration (by adding assemblies or search directories) to clean up any of the errors before moving on to looking at the analysis results. There are a few valid C# language constructs that NDepend's source code parser won't recognize. I got a warning from a class constraint on a generic type. I understand this will be fixed in a future version. You can ignore these warnings; they're harmless.

  • Whatever you want to do within the application, the place to start is by selecting the appropriate view configuration from the View menu. So if you want to alter a query to return more specific results, you would select View -> Reset Views to work with the CQL language.

  • The CQL Query Result tab will display a query, and has been Edit button, you can never added a query in that window. To edit a query, go to the CQL Queries tab, select the query you'd like to edit, and click the Edit Query button. A new tab will appear which allows you to actually edit query.

  • When you click on a query in the CQL Queries window, you will only see the results if the query returns any rows. If the query returns no rows, then the results window will not appear.

  • Whenever you analyze the project in the NDepend GUI, NDepend creates a lengthy HTML report, and opens it in your web browser. But all of the information in the report is also available within the GUI application, as well. So you can see the same information in whichever place is most convenient for you to navigate.


Performance


NDepend is fast, sometimes surprisingly so, given that it is looking at both binaries and source code. It generally takes less time to run an analysis than it does to compile a solution. I never experienced any performance problems in the NDepend GUI, either.

Conclusion


Do you need NDepend? It depends. NDepend will be useful to developers and teams who have a functioning continuous integration process (and you should) and want to impose standards on this sort of code which can be checked in to the repository. But if you're going to go down this road, you should probably start with FxCop, which is free and better known. NDepend, however, can take this kind of analysis much, much farther than would be possible even with custom FxCop rules, like comparing multiple versions of an application and mandating unit test coverage. I also find it much easier to customize.

If you do have an automated build procedure in place and you're not already doing static analysis, it's time to start! It can be a bit overwhelming to run static analysis for the first time on a legacy project and see hundreds of warnings or errors. But that's easy to get over; simply disable all the warnings but one, and start cleaning things up a bit at a time. You can make the analysis stricter as time goes on. You don't have to start by fixing everything. Even a few rules in place will prevent new such errors being introduced to the source base.

It will be also useful to developers confronting a large source base for the first time, particularly if there are more than a few assemblies involved.

While NDepend might be useful to a single developer reviewing their own code, and not part of an automated process, I've found that static analysis will really only be used regularly when it is automated. Few developers have the discipline to run static analysis and cleanup any problems uncovered without integrating it into build automation.

Disclaimer


This review is based on a review copy supplied by the makers of NDepend. If you think that has colored my impressions of the tool, well, I disagree, but at least you know.


Comments

Check out more tips and tricks in this development video: