Let me introduce you to my go-to code visualization and analysis tool for .NET code: NDepend.
What is NDepend?
NDepend is a commercial software product that analyzes .NET code in any language, visualizes code issues and structure, and monitors code issues and other metrics over time.
I’m a tools nerd. I always have been. I’m also an extremely visual thinker. As a .NET development manager and architect, NDepend is perfect for me.
Let’s look at what NDepend is and why it might be right for you and the people you work with. I’ll pay special attention to NDepend’s role in helping with technical debt and improving software quality.
Disclaimer: While I have previously been a paid NDepend customer, I am reviewing NDepend with a free copy for review purposes.
NDepend’s visualization capabilities are the strongest point of the offering in terms of prioritizing tech debt and communicating code quality.
Let’s walk through the key areas NDepend helps us understand.
Recently, I wrote an entire article on the role Cyclomatic Complexity plays in maintainability, testability, and quality of software.
NDepend helps fight complexity by giving us a tools to visualize software complexity by area.
Below is an image of NDepend’s metric view analyzing my hobbyist game development project from last year:
In this tree view, each rectangle is a method in the application, grouped by class, namespace, and assembly. The color of each rectangle is a scale based on the method’s current cyclomatic complexity.
The view is very customizable. You can change gradient colors (useful for those with red / green colorblindness) and color transition points. You can also choose which metric drives color and which metric drives size. Additionally, you can toggle whether labels appear only over the item your mouse is over or over most major areas.
This view is without a doubt my favorite aspect of the application. It helps zero in on the areas needing the most attention, and it generates visuals that are critical to helping others “get it”. This is important because communicating technical debt to business stakeholders is hard, but very important.
Unfortunately, cyclomatic complexity is only one aspect of software complexity and it tends to over-emphasize switch statements while under-emphasizing other sources of complexity. That said, it can be a good starting point when prioritizing technical debt.
That same tree map view can be configured to colorize methods (or types) based on their % of code comments. This helps find documentation and knowledge gaps.
My hobbyist project, pictured above, might be described as “significantly lacking” in documentation. Then again, its complexity is low, and the test coverage is good, so documentation is actually not a huge problem.
What you want to do is look at different facets of your own code. Highly complex code that is frequently modified, lacks unit tests, and is undocumented is probably something to be concerned about, for example.
Code Coverage metrics can be included in NDepend’s results. If present, code coverage will enable rules about covering code and covering newly added code. Code Coverage also appears prominently in major areas of the application.
Setting Up Code Coverage
The coverage data needs to come from outside of NDepend since NDepend doesn’t interact directly with unit tests. Currently supported code coverage providers include:
- dotCover / ReSharper Ultimate
- Visual Studio Code Coverage
I’m a habitual ReSharper user, so that’s what I use with working with NDepend on the desktop.
This process with ReSharper is a little cumbersome at the moment. Each time you regenerate coverage, you have to go through a series of steps:
Note: I have reached out to JetBrains for comment on if this flow will be improved in the near future. I had not received a reply by time of publication.
Once the coverage data is present it’s easy to integrate. You can click
Import Code Coverage Data from the main dashboard and then select the directory or a specific code coverage file:
If the flow above looked somewhat tedious, I strongly recommend you integrate NDepend into your build process and use the free OpenCover tool to generate coverage data. This is a simple and streamlined experience that you can set up once and forget. Once you do, coverage details will be tracked constantly and available in the generated reports on the build server.
Code Coverage Visualization
Once coverage data is configured, you can run your report and code coverage data will show up as expected:
The various trend charts will also include the % coverage as a green shaded area by default:
The same tree view we used for visualizing complexity can also show us the code coverage percentages by method or type:
This view is a homing beacon for locating parts of code that are untested or not tested enough. It also can find parts of code that are simply no longer being used. This value should not be underestimated; I was able to remove 20,000 lines of dead code from an enterprise application thanks in part to this view.
Note that NDepend also has rules identifying dead types and dead methods, which gives you helpful lists to investigate.
Okay, you get it – I’m in love with the tree metrics view. What else does NDepend give us?
Well, for starters, you can use it to visualize dependencies between projects:
In this case, I have a small solution with only a few projects so the view is somewhat underwhelming. However, if I wanted to drill into how two assemblies interact, that’s fairly easy to do:
From there I can drill into an individual member and get more details or view its source code.
Honestly, it’s hard for me to fathom why I haven’t been using this view more to visualize how code interacts with each other.
The same data in the dependencies graph view can also be viewed in a matrix or grid view:
To me, this view is harder to read, but it does highlight problem areas between parts of your application that are mutually dependent on one another.
This is where one of NDepend’s strongest opinions manifests itself: enforcing dependencies between namespaces. NDepend will warn you in this view (see red boxes above) if you have namespaces that are mutually dependent on one another.
If you find any interactions particularly troubling, you can right click and create a new rule prohibiting that relationship.
Tracking Metrics over Time
NDepend features a dashboard that allows you to view how your code has changed over time. It also highlights changes relative to a baseline so you have a good view on what direction your code is trending.
This view helps you quickly get a sense of the health of your code. Like most areas of NDepend, you can nearly anything drill into more detail.
Charts automatically update each time you run the report and track key metrics over time. This can be helpful in communicating to business stakeholders that your work on debt is actually producing results.
Sharing Metrics via Reports
All of this data is great, but sometimes you need to share it. NDepend, thankfully gives us an option for sharing its results with people who don’t have the tool installed.
NDepend can generate HTML reports as it analyzes code. These reports (pictured below) contain most of the same data available in NDepend in either an image or lightly-interactive format.
Additionally, if you integrate NDepend with your build server, these same reports are available there.
While I’d love an interactive tree view in the reports, right now the tree view is just a static image.
The other thing high on my wish list would be the ability to define multiple tree views to include on the report. At the moment if you have coverage data, the tree view included will be code coverage. If no coverage is present, you’ll see the complexity metric. These two metrics are actually really interesting to look at together since one encompasses risk and the other represents a safety net.
Code Analysis Rules
NDepend’s main advertised feature is its ability to identify and detect issues in code. While it does do this, and it does it fairly well, I don’t think this is NDepend’s unique value proposition and so I’ve left it to this point in the article.
That said, NDepend has a very robust rules engine built around a custom query language. This language is based on LINQ and all pre-defined rules can be modified or disabled at will:
I do believe that most users won’t use this level of customization, since the default rules are very good. However, the customization options are present if you want them.
NDepend integrates with a number of different build servers and code coverage providers out of the box.
Additionally, NDepend can integrate directly into Visual Studio or function as a stand-alone application.
Visual Studio Integration
NDepend includes an extension to integrate into Visual Studio. This offers some automatic improvements such as including projects in your solution automatically into a new project, filtering out test projects by default, and re-analyzing every time you build.
The downside of this is that if you’re working on a laptop, screen space can be a little lacking:
Visual Studio integration gives you the ability to right click on methods and properties and view them on the NDepend graph. This is a very interactive way of taking advantage of the graph of code that NDepend maintains.
NDepend comes with its own dedicated IDE built on the Visual Studio platform:
The UI will be familiar to Visual Studio users, and makes options more visible with full use of the menu and toolbars. Your Visual Studio theme will also be applied to the editor, so that consistency is nice. Additionally, the stand-alone version is very helpful for context switching and maximizing screen space on a laptop.
Build Server Integration
NDepend integrates with a number of build servers, if you purchase an edition with build server support.
The currently supported build servers are:
- Azure DevOps
Build server versions of NDepend also include a console application that can be run by other build servers.
I strongly recommend integrating with a build server. When I hear people talking about NDepend, one of the things I hear the most is “I love it, but I don’t remember to use it enough”.
If you integrate into your build server, the metrics are automatically gathered and stored in a central place. Additionally, more people are going to be able to access the results of analysis. This is very useful for managers or directors wanting to check in on technical debt on an infrequent basis.
Being able to establish firm quality rules for your team that fail a build if violated has value as well.
The Effects of using NDepend
Okay, so now you know what NDepend is and the value it offers. Let’s talk about the effects NDepend can have.
First, NDepend raises the level of awareness of bad code inside and outside of the development team.
Secondly, NDepend gets developers thinking about architecture, dependencies, quality, code smells, etc. It teaches developers how to spot bad code and challenges them to grow.
On a personal note, I discovered NDepend years back while I was transitioning back into the workforce from long-term disability. NDepend gave me something to do and got me thinking about software architecture and quality. It genuinely made me a better developer and I credit some of my talents to these effects.
NDepend and Perfectionism
On the flip side, NDepend can become a bit of a distraction. There’s a trade off between tracking quality, paying down technical debt, and keeping the business and customers happy by shipping features and identifying bugs.
There’s a tendency in software developers to strive for perfection. You’re likely never going to get a codebase completely free of any code issue. That’s okay. Learn to accept it. Prioritize the things that need to be prioritized. Don’t let the tool or its metrics slow you down. Instead, keep charging ahead, but use it to focus on what you want to focus on.
This, ultimately, is what caused me to stop using NDepend years back. My productivity suffered as well as the tool itself became my focus instead of building code.
It should be noted, however, that my use case was not the recommended one. The creators of NDepend recommend that NDepend be used as a tool to prevent future quality deterioration, not something to help stop and address all current quality concerns.
This is the main premise behind the baselining feature and focus on newly changed code in many of the rules. The idea is that you baseline before you make changes, then NDepend analysis looks for newly introduced issues on analysis. This makes NDepend a prime ally in making sure code at least is not getting worse.
At a bit under $500 USD, I wish that there was a cheaper “community edition” or other licensing option available to individuals purchasing on their own instead of through companies. Additionally, I’m a firm believer in trying tools and techniques at home on side projects before trying to integrate them into a major project at work.
I believe the effects of NDepend on a seasoned developer’s thinking skills are incredibly profound. NDepend is absolutely worth the money if you have the budget for it. It’s going to help you get better at what you do and your code will get better (and keep getting better).
On top of that, your ability to communicate code concerns to the business will improve significantly as well. NDepend is currently my go-to tool for showing product management, quality assurance, or upper management issues hindering our development speed or significant sources of quality risk.
Ultimately, NDepend serves a critical role in a .NET development organization. This tool is crucial to understanding the quality of your code over time and communicating that to key stakeholders.
As far as I’m concerned, NDepend is the analysis tool you give your .NET architects and lead engineers. Let those key people set up the rules the way they like them. Once the rules are set, integrate NDepend into your build process so everyone can track the results over time.
After all, we all want to ship quality software, and NDepend helps us do that over long periods of time.