When I started my professional software development career, it was in a small team of developers. Every member of the team was usually working on their own project, and sometimes we worked together. A regular topic of discussion at the bi-weekly department meeting was that of code quality. Most of the times this resulted in the promise to do peer reviewing of code. And most of the times, that’s all that it amounted to, a promise. Various excuses popped up: “too busy with my own project”, “on whose budget should be write those hours reviewing?” , “it’s not my domain expertise”, “the software works, so why bother”.
Whenever we did some sort of review, there was a tendency to treat it as perfunctory. A rudimentary check of the modified code. A comment on any obvious mistakes or just call out a few lines to look busy.
While we all understood the need for writing good quality code, in interest of maintainability and keeping down the number of bugs, there are problems in ensuring it. It’s not just a matter of finding the time and getting everyone to do it. A problem with quality is that it’s subjective. First the team needs to agree on the practices which define quality. Then, you need a form of measurement to reveal the level of compliance which has been achieved. We are talking about indicators on the various aspects of the code. Finally, the process of measuring quality needs to be repeatable. If you want code to improve, you need to be able to repeat the measurement to know if modifications are made in the right direction. Preferably, this process is not time consuming.
Fortunately, in the past 10 years, we have seen an increase in tools that help developers and teams to achieve just that: measure a set of quality objectives that can be quickly collected and reported. One them is NDepend.
A couple of weeks ago I received an email from Patrick Smacchia, the author of NDepend, asking if I would like to try out NDepend. As it happens, it’s been on my mind the last few months to delve into code quality as a topic of interest for my blog.
NDepend is a static analysis tool for .NET Managed code that can run either on its own or as a plugin for Visual Studio. Static analysis, in contrast with dynamic analysis, is performed purely on the source code without executing it. It’s useful both during design time but can also be applied across an existing project. The latest version has 86 code metrics which are analyzed and reported on by the tool. You can download a free trial that lasts 14 days. The trial is fully functional, and if you appreciate the features, you can get a license that includes one year of technical support for 399 euros ($ 477).
In this post, I will run NDepend on a project that I’ve built a couple of years ago and have been maintaining since. Evolving insight, newer versions of C# and (perhaps dirty) bugfixes make it a pretty standard solution. The solutions consists of 8 projects, with two of them ASP.NET MVC web applications. Obviously, the build result is this:.
========== Rebuild All: 8 succeeded, 0 failed, 0 skipped ==========
Once installed, NDepend makes it easy to get started. There’s a new menu item “NDepend” with the first option allowing you to attach a new NDepend project to the active solution.
If you click the menu item, you will see a list of assemblies generated by the project and available for analysis.
This project has a public accessible web front end and a web front end acting as control panel. Business logic and data access is handled in the Model-project. By default, NDepend will create its own project file with a .ndproj extension in the root of the solution where it will store configuration information. I’ve left the ” Build Report” box checked, so after confirming this dialog NDepend will do it’s thing. If I don’t keep the box checked, it will still analyze the assemblies but not create a report, just a small list of Errors, Warnings and Informational messages. If you later on decide you want that report anyway, you can choose Analyze and Build Report from the menu.
The report is an HTML document listing every single finding. As a first time user, NDepend tries to guide you through the various features.
What you decide to do next depends on what your really want from the tool. The report is a great document for keeping the current state of the app on record and use it as a starting point for evaluating quality. It all gets saved in the NDependOut folder so it can be zipped and redistributed if necessary. Here’s the summary page for my project:
You will notice that the NDepend is not very pleased with my code. If you run the tool yourself in Visual Studio, you will also notice a red symbol at the bottom right of the IDE. In the case of this project, the dot is red. If you hover over the dot, you see a summary of the report. Clicking on it will give you the full report in a Visual Studio window.
A Quality Gate is a check on a code quality fact that must be enforced before releasing and eventually, before committing to source control. A Quality Gate can be seen as a PASS/FAIL criterion for software quality. Given that the code apparently fails on this check, I am eager to find out where the problem is located. However, Quality Gates represent a synthesized view of simply PASS or FAIL. We need to find out what the underlying issues are.
Digging a little deeper
For this purpose, NDepend has a Queries and Rules Explorer.
If you select the Quality Gates node in the left pane, the related rules are shown in the right pane. From there, you can select the 3 issues that it found. Here, the navigation becomes a bit small.
If you hover over an issue, some additional information is displayed.
Unfortunately, it’s not really showing where the problem is. We need to dig deeper. Double-clicking on the rule gets us a step closer.
Apparently, there’s an issue with the class CrawlerLog. If we go back to the rule the code seems to violate, it says “Avoid duplicating a type definition across assemblies”. Unfortunately, it’s not immediately clear why this issue popped up. Double-clicking on the issue tells me that the class is declared in two source files.
This is quite normal, since the first is a generated file, generated by Entity Framework. The second file holds a partial class declaration with some added properties and methods. It is doubtful that the analysis result would complain about a useful compiler feature such as partial classes. So where exactly lies the problem? It turns out you can click on the cell for the typeDefs column. Before we do that, let’s take a step back at the way we navigate through the various windows.
Navigating through the information
NDepend adds 12 new windows to Visual Studio. Even though I’m currently using three monitors for my daily work, the amount of time wasted by managing and navigating through the various windows in Visual Studio is starting to be my main frustration. NDepend adds to that. As a first time user of NDepend, it’s quite a massive tool. The best way to maintain focus on your analysis is to dock the Queries and Rules Explorer and the Queries and Rules Edit windows to a second monitor so you can keep Visual Studio on your primary screen. The information density on the windows is quite large. As a web developer for consumer facing websites, I often interact with designers whose main role is to make sure that the amount of content can be reasonably handled by the user without being bogged down by it. The more content a user encounters, the longer it will take them to complete particular tasks. Too much information density will result in analysis paralysis for users and good design means the user knows what they should do at every point of interaction. If they’re getting frozen in their actions because too much information is confusing them, you’ve got a problem.
The amount of information NDepend delivers is pretty much dependent on how many issues are found. If there are many issues in your code, you will likely be overwhelmed. Fortunately, you are not required to fix all the issues immediately to keep going forward with your project. Fixing everything first would be a no-go for your project lead. You can therefore use the first analysis as a baseline, and compare subsequent analysis runs against that baseline. This way, you only have to focus on new problems that should be more easy to fix.
Back to the issue. I found out that it’s possible to click on the cell in the column typeDefs.
Now I see where the problem is. Apparently, somehow a file was copied into a wrong project. It’s not only in the Model-project where it belongs, but there’s another version hanging around in the Web-project where it’s shouldn’t be. It was a simple matter of removing the file to satisfy the violated rule.
A rerun of the analysis shows that there are still six rules that have critical violations. Critical rules are defined as high priority rules that must never be violated. By default, a number of these rules are marked as critical. But you can easily unmark them. Since my project holds two ASP.NET MVC Projects (a public facing website and a back-end control panel), a total of 32 issues are found do to this simple rule: Avoid having different types with same name.
The reason for this violation is simple, and I have no intention of merging the two web-projects or moving about the view-models to a common project. There is surely some duplicate code, but since most of it is generated anyway, I’m happy to leave it as such. To unmark a rule as critical, click on the “Critical” toolbar item and save the rule.
Changing the rules
86 different code metrics is pretty extensive. It would be no problem to start with a brand new project and have NDepend watching closely to alert you of deviations of good practice as you build up your code. Since we usually deal with existing projects, you need to be selective. You can, for instance, look at the Code Metrics View to pinpoint problematic assemblies and types in your project. The view allows you to look at the various code metrics, like cyclomatic complexity, number of parameters, and code commenting from a higher level.
Each of the rules has a rationale behind it. Take the one that uses the metric of number of lines of code for example. The rule is that types should not be too big. The rationale behind it being that large classes are hard to maintain. The question of course is, what amount of code is too big. Each rule is coded in CQLinq or Code Query Linq. A CQLinq query is a LINQ query that relies on types defined in the namespace NDepend.CodeModel of the NDepend.API. You can modify the queries, and thus the rules. Looking at rule “Avoid types too big” you can see that it checks on types where the lines of code is more than 200.
warnif count > 0 from t in JustMyCode.Types where
// First filter on type to optimize
t.NbLinesOfCode > 200
So to satisfy the rule violation, you either fix the classes with more than 200 lines of code, or you change the threshold to the more common value in your project. If you change every single rule to satisfy your project, you are probably not taking the recommendations very seriously and perhaps should not bother with a tool like NDepend.
Besides giving a thorough analysis of your code, NDepend also helps in understanding the structure of your solution by means of various visual representations. These visual representations of your code are pretty neat ways to get a quick feeling of code structure without actually delving into the syntax itself. There are a number of tools around to do this. Visual Studio 2017 itself comes with sequence diagrams and architecture explorer graphs baked in if you invest in the Enterprise edition. I’m not going to touch on the various visualizations, partly because the real power of these various graphic representations involve interactions that you need to see in a video, but also because there are explained in detail at https://www.ndepend.com/docs/visual-studio-dependency-graph and https://www.ndepend.com/docs/dependency-structure-matrix-dsm.
While it’s nice to have the various analysis en exploration features of NDepend available in Visual Studio, that interaction does not really work in a continuous integration scenario. Not to worry, though, because you can also analyze source code and .NET assemblies through NDepend.Console.exe. With this, you can integrate NDepend analysis into your build process and you will be advised automatically of the status of your code. There is also a separate extension that contains a build task to analyze code and code coverage data: https://marketplace.visualstudio.com/items?itemName=ndepend.ndependextension
What about Roslyn?
Visual Studio 2017 includes a built-in set of .NET Compiler Platform analyzers that analyze your C# or Visual Basic code as you type. You can install additional analyzers as a Visual Studio extension, or on a per-project basis as a NuGet package. Analyzers look at code style, code quality and maintainability, code design, and other issues. These .NET Compiler Platform (“Roslyn”) analyzers will eventually replace static code analysis for managed code. Many of the static code analysis rules have already been rewritten as Roslyn analyzer diagnostics. But they are not easy to write. Certainly not as easy as the CQLinq queries in NDepend.
So while Roslyn is helpful during your daily coding tasks, it does not replace NDepend. The Roslyn analyzers help you while coding as they can fix issues with a single click. NDepend will keep track of the quality of your code as the project moves along. Both helpful.
Installing and using NDepend will not suddenly solve your code quality issues. It will not fix a project with too much legacy. However, if you or your team members struggle with peer reviews, due to lack of time, focus or a common ruleset, NDepend can certainly help with that. Be aware that the scope of the analysis is assemblies, so it will not comment on the quality of a user interface or the way the data is modelled. It will, however, show you problems about class usage and interactions.
When you first run an analysis on a project you have been working on for some time, be prepared to get some serious amount of recommendations. While you may not agree with each of these, it’s good to know that the rules been created and tweaked with a lot of thought over a period of more than 10 years. Before you go about and dismiss one, check out the rationale behind it as every rule has extensive comments, references and documentation to make sure you understand why following a particular rule would improve the quality of your code. Another way to cope with the amount of issues is to use the first analysis as a baseline and use the diff feature. This way, you can focus only on new issues and not old ones.
In a world increasingly dominated by open source, and consequently mostly free, software, you may be hesitant (or constraint by a budget) to pay the 399 euros for a license. NDepend, however, does a good job in telling you how much time you will probably spend in the long run fixing issues that could have been avoided by proper design and coding rules. And if it’s too late, and the coding issues are already there, the analysis will show you how much time you will likely need to fix it now and avoid future costs. So there’s an opportunity to download the 14 day trial version that has no limitations in features to check out your code. This way, you can quickly see if the price of a license will be covered by having better code quality. You cannot express that value of code quality (tooling) better than that.