Using NDepend
12/03/2019
7 minutes
In this post, I am going get started with a tool called NDepend. NDepend is a static analysis tool, for .net developers, which helps to understand different levels of dependencies in the code. It also provides code metrics, technical debt estimation etc, among many interesting features. I have been looking forward trying it, and in this post, I will have a first glance.
I have a chatbot application to help me with the public transportation waiting times. The core logic of this application has been moved to a class library, but it has been powering a Windows Mobile application, an ASP.Net Core website, a Service Fabric application and an Azure Functions app. Every time, I had hosted in a new platform, I had small change and refactorings on the API, though there should be a good amount of tech debt piled up. Let's see how NDepend can help me find these issues.
Getting Started
NDepend can be installed as a Visual Studio extension or as a standalone app. I have tried both versions. For iterative work, it is handy to have the tool installed as a VS extension, but otherwise, I prefer the standalone executable version of the tool. There is a free trial version and a licensed version as well.
Here is a typical workflow with NDepend:
Open NDepend
Open the solution (or folders with dll-s).
Run the analysis
Interpret results
Refactor the code (run your tests)
Repeat from the analysis step
In the next part of the post, I describe an itereation using this tool. First, open the solution in NDepend for analysis, and filter down the projects to a relavent subset for the investigation.
Unfortunately, the tool could not locate all the assemblies for the Service Fabric projects based on the solution file.
Interpreting (some of the) results
NDepend is an extremely in-depth tool. It provides several metrics and visualizations, hence the the user needs to have a good understanding of these metrics, before he/she can draw further conclusions about the code. As this is my first usage of the tool, I will pick one of the visualizations for this post but expect some further posts to cover some other metrics and views as well.
Fortunatly, there are several online videos to get started with the tool, but I found only one Pluralsight course which touches NDepend at the time of writing this post. Is this a great opportunity for content creators?
Let's see the results of the first analysis:
Ok, at first glance it says <5% of dept and a rating of A. I was expecting a little more, though later looking at the rules, and suggestions I see plenty of opportunity to improve. The code has 2 Quality gate failures. Let's dig into these:
It says Debt Rating per Namespace
, not sure what this means, so let's drill into by clicking it. The rule says:
Forbid namespaces with a poor Debt Rating equals to E or D. ... The Debt Ratio of a code element is a percentage of Debt Amount (in floating man-days) compared to the estimated effort to develop the code element (also in floating man-days). The estimated effort to develop the code element is inferred from the code elements number of lines of code, and from the project Debt Settings parameters estimated number of man-days to develop 1000 logical lines of code. The logical lines of code corresponds to the number of debug breakpoints in a method and doesn't depend on code formatting nor comments. The Quality Gate can be modified to match assemblies, types or methods with a poor Debt Rating, instead of matching namespaces.
Ok, so it says I just have too much dept here, let's see the code. It consists of 2 classes with constant value declarations.
Refactoring the code
First Gate Failure
So, the first issues, is easy to solve. Some of the given types are not used at all. Fix is easy, simply removing them.
In the second case, it suggests avoiding publicly visible constant fields. I was about to provide the reason for it here, why this is suggested, but the tool is kind enough to show it as well:
This rule warns about constant fields that are visible outside their parent assembly. Such field, when used from outside its parent assembly, has its constant value hard-coded into the client assembly. Hence, when changing the field's value, it is mandatory to recompile all assemblies that consume the field, else the program will run with different constant values in-memory. Certainly in such situation bugs are lurking.
Again, simple change by lowering the scope of visibility, which I can do here easly, as these constants are specific to the chatbot only:
internal static class Inputs { internal const string Choice = "ChoicePrompt"; internal const string ChoiceMultiChannel = "ChoiceMultiChannelPrompt"; internal const string Confirm = "ConfirmPrompt"; internal const string Location = "LocationPrompt"; }
At this point, I built the source again, which also refreshed the NDepend analysis results: 1 quality gate less.
Second Gate Failure
The second quality gate says: Critical rule violations
, let's see what is inside.
There are three rules violated, let me address each of them, one-by-one.
Avoid methods with too many parameters: the rule identifies the constructors with more than 6 parameters and flags them. In this case I just hit the limit. I myself remember this method during implementation and thinking about creating a facade. Now the time has come to do it.
public ScheduleBaseDialog(string name, ITravelRouteServiceFactory travelRouteServiceFactory, ConversationState conversationState, UserState userState, ISharedDialogService sharedDialogService, RequestLocationDialog requestLocationDialog, ILogger logger) : base(name)
In this case the subdialogs could be moved to a subdialog collection:
public ScheduleBaseDialog(string name, ITravelRouteServiceFactory travelRouteServiceFactory, ConversationState conversationState, UserState userState, ScheduleSubDialogCollection scheduleSubDialogCollection, ILogger logger) : base(name)
Let's run the analysis again, to confirm the violation is resolved.
The next rule on the list: Avoid non-readonly static fields: that was a simple change again. A missing readonly on a static field, which was initialized inline.
Finally, Avoid namespaces mutually dependent. Running into this rule meant cleaning up namespaces and moving classes to their proper namespace.Fortunately in my case, I had the classes defined with clear dependency structure, but as they have been moved around to different folders with time, their namespaces did not match. Hence fixing the namespaces in this case was relatively simple. When an application has spaghetti code, this rule would be more involved to fix.
Running the analysis once more after addressing the issues shows debt has been reduced to half:
As in each iteration NDepend compares results with the previous run, it some additional quality gate violations might appear. For example changing public API will result a Critical rule violation. To disable this comparison, the user can select a baseline as none
, which comes pretty handy. Note, that in this case public API violations were acceptable, as I am the only user of this codebase.
Conclusion
NDepend gives a nice summary on some of the tech debt hidden in projects, that have lived through multiple lifecycles of the software.
This is just getting started with NDepend. I am looking forward using it more-and-more in my daily routine. The reader of this blog may expect to see some further experience and hands-on tips in my future posts as well.
I would like to say thanks to NDepend for sponsoring this post by providing a license for their tool.