Find if NDepend can improve your code quality

On how you could improve internal quality with NDepend.

You have probably seen this meme offering services that could be any two of good, cheap, or fast:

When I first saw it, I only found it funny. But now I see how clever it is. It alludes to three kinds of expenses every software team has to think about: Quality, Money, Time.

Martin Fowler wrote a post a couple years back titled "Is High Quality Software Worth the Cost?". In summary:

  1. He makes a distinction between external quality (such as the UI and defects) and internal (architecture) quality.

    users and customers can see what makes a software product have high external quality, but cannot tell the difference between higher or lower internal quality.

  2. He shows that the cost of internal quality is negative.
  3. And concludes that high internal quality makes for faster AND cheaper software development (over a reasonable period of time).

The last point, in particular, directly contradicts the meme, doesn't it?

Patrick Smacchia emailed a few years back asking me to try NDepend**, and I've been playing around with it through 2021.

About NDepend

NDepend is a tool focused on improving your .NET code quality.

Now if you search through the internet, you'll see a ton of posts talking about NDepend's features. So, I thought, for this post, I'd explore how the tool could possibly help you or your team write better code. And how a team could go about deciding whether they could benefit from NDepend.

Here are a few things about NDepend that I thought was cool:

Estimating dev effort associated with technical debt

Like every other debt, technical debt could be okay if we're responsible about it. The simplest way to reason about introducing low-quality code is to weigh its short-term benefits with long-term consequences.

Let's say you are working on prototype feature. Building the feature well would require you to either refactor some existing code or write some unclean code. If you choose to start with writing some unclean code, NDepend can now help you estimate how long it'll take to get that code in order.

Helps focus on not introducing new debt

It's not usually possible for a team to decide that they're gonna pay of all their technical debts in the next n days. It doesn't work that way. It's often wiser to first start by not introducing any new debt, and then identify patterns in existing well-written code, and then see how we could clean old code up. It takes time.

This is precisely the problem with .NET Analyzers. They are perfect if the code has near-zero debt, and also work just fine for new projects. But if you own a code base with a lot of technical debt, enabling all .NET Analyzer rules is sure to spit a gazillion warnings.

This is one place where NDepend shines. It allows you to look at new debt introduced over time by allowing you to compare with a "baseline". This makes it easier to write clean code incrementally.

.NET analyzers also encourage you to maintain code quality over the entire codebase. With NDepend, it makes it possible to prioritize certain portions of the code over the others instead.

Makes it easier to approach a new codebase

NDepend also allows you to generate a bunch of graphs that can act as a map to your application. The way files are laid in a project can tell you a lot about what's going on, but visualizations make it easier to create a mental model.

This is one of those features that even developers new to C# can appreciate.

Let's say I wanted to use MassTransit's SignalR integration. If I was new to MassTransit, I could clone the repository and look at the dependency graph.

The following graph tells me that MassTransit.SignalR takes a dependency on MassTransit.ExtensionsDependencyInjectionIntegration (the default Microsoft DI container).

If I wanted to know about where the ISendTransport type is being used within MassTransit, the traditional Visual Studio way would be to use the references link above the type name:

But a graph can tell you a complete story:

A portion of a call graph generated with NDepend

This graph tells me that MassTransit.SignalR does not use ISendEndpoint directly or indirectly.

Tips to evaluate NDepend

NDepend is not for everyone. And so, I've come up with two steps to help you evaluate if it will benefit you or your team, and a bonus third step to plan the first phase of improving code quality with your team.

A relatively senior engineer that cares about improving code quality should ideally lead the evaluation.

I. Look at your code the way NDepend sees it

  1. Download an NDepend evaluation copy and install it on Visual Studio.
  2. Open one of your solutions, and click on the circle at the lower right corner to create a new NDepend project (*.ndproj).
  3. Browse to a path on the same drive as the VS solution. It defaults to the solution path, but I like to change it.
  4. If you have more VS solutions are part of the same overall codebase, click on the "No assembly outside the VS solution will be analyzed" tab and click on "Add assemblies of VS Solutions" button to add more solutions.
  5. Now click on the Analyze N .NET assemblies button on the lower right corner. You should now be presented with a dashboard.
  6. Press Alt+G or use the VS menu bar and go to Extensions > NDepend > Dependency Graph.
  7. Hover around the graph. Take enough time exploring. (If you have a lot going on, you might want to use Layout > Box Size > Constant to make the graph fit on your screen.)

Was NDepend able to reveal something you either did not know?
Was NDepend able to reveal something you knew but never had a chance to talk to your team about?

If you answered no to both questions, you're probably better off with plain .NET Analyzers at this point. You may re-evaluate NDepend better.

But if you answered yes, make a note of what NDepend revealed. You'll need it in step 3.

II. Look at the issues in critical parts of your codebase

  1. Go to Extensions > NDepend > New Issues since Baseline, and click Ok.
  2. In the Queries and Rules Edit pane, click on View source code and delete the where i.WasAdded() clause.
  3. You should now see a list of issues grouped by assemblies. Browse the issues in some of the most important assemblies you are familiar with.

Take enough time and go through issues and its corresponding "how to fix" recommendations.

Was NDepend able to flag issues that your team cares about?
Was NDepend able to flag issues you know your team should be caring about?

If the answer is no to both of the above issues, you won't need NDepend unless you want its visualizing capabilities.

But if you answered yes here, NDepend is probably going to be worth a shot.

III. Work on a plan

At this point, you should have about 5 to 9 working days left on your NDepend evaluation period.

Get on a team call and try to have an honest discussion about code quality. Define your code quality as one of:

  1. The code is great, because the team is. Too cool for NDepend.
  2. The code is not good enough, but the team is. We got this. NDepend might help us get there faster.
  3. The code is fine. The team is ok too, but some help might make us better.

I'd only recommend NDepend for a team that falls in category #2.
If this is you, pick a portion of your codebase (it could be an assembly or a namespace) and decide which NDepend rules matter and which do not. It is possible that step I. of evaluation uncovered structural problems with the way the application was architected; if this is the case, talk about it on the call and decide next steps.

If the team is on #3, you might want to consider getting someone that could help the team. While NDepend might still help you, it cannot give you the sense of direction another experienced engineer can. (Besides, if a team is capable of saying it's not good enough, isn't it already on its path to getting better?)

Final observations

I did not know what to do with NDepend when I started, but I slowly began to appreciate it over time. It does not have a steep learning curve, but it does take a while to figure how it could fit into your workflow.

If you find yourself diving into new codebases ever so often my guess is that you'll appreciate this tool. On the other hand, if you know your codebase through and through, NDepend may or may not suit your specific needs but is certainly worth evaluating.

Lastly, if you've been considering NDepend, I hope this post helps you make the most of the free trial period.

**I was given a year-long license to evaluate NDepend, but this post is not paid or sponsored by NDepend: the opinion expressed here is mine. ()