Using nDepend to understand, and improve legacy code

One of the issues when dealing with legacy code is missing information. Unlike newly developed code which has up to date documentation or at least some developers who know design decisions and implementation details. Legacy code is a completely different story – the original developers usually are not around to answer any questions, documentation is missing or just plain wrong. And if by some fluke of luck you’ll manage to find one of the original development team, most chances that he does not even remember most of the crucial information.

This is why is nDepend is one of my all time favorite tools. It helps me understand huge amounts of code quickly, and helps me find code issues so I can fix.

Right off the bat you can open a solution or an assembly folder, this is a useful feature especially when dealing with legacy codebases where it’s not always clear how to build each dependency. It helps me analyze projects that are built from multiple sources, in the worst case you can copy the files directly from the server and point nDepend on it.

On of the most important steps of understanding and improving existing code is mapping dependencies – understanding which objects use other objects will help you find issues in which a small change could make a big difference.

Dependency Graph

After you finish running nDepend for the first time you might be overwhelmed with the huge amount of data provided. This is the reason I usually work my way from high-level diagrams to low level details. I found that it helps to start with the dependency graph. This is the 10,000 feet view of your code. If the diagram has a small amount of objects and a clear dependency flow (no cyclic dependencies) – you’re good to go. If however you have one huge node which all of the other nodes depend on – you might consider splitting it into classes. If you’re really unlucky you’ll something like this:

Oh dear...

Looking at this graph you know you’re not going to have a good day…

The size of the nodes also matters – the bigger they are the more code you might have and so if you have one huge node used by all of other, smaller nodes – you should consider splitting it into different classes. Consider the following graph – what do you think would happen each time you need to change something in the big “junction” node in the middle of the graph?

InkedComponentDependenciesDiagram_LI

It looks like half of the system uses it while it dependent of the other half of the system – this might be something we need to look into…

Dependency Matrix

The next tool to check is the dependency matrix. Here we can see usage between modules and find where do we need to concentrate our efforts.

Take the following result for example. A quick glance will tell you that there might be a problem there:

InkedComponentDependenciesMatrix_LI.jpg

Hint: rhymes with “-ommon”

Looking at the matrix we can tell that there are multiple calls from the BusinessLogic assembly to dependency number five – i.e. Common. The name is also a dead giveaway since Common/Utility/Helper classes tend to be a container (e.g. garbage can) in which all static methods are thrown one on top of the other without any order or logic.

The Rules

After we have a good idea what is happening in the code and hopefully by now we understand the code structure better and we can finally dive into the code.

The cool feature about nDepend is that it’s filled with checks and rules that would show you issues and violations and even help you understand how to improve your code. At this stage nDepend help me find a lot of “dead code” – code which is not used and can be deleted. While also pointing out problematic violations that should be addressed. This part of the report helps me focus on.

Rules

This list helps me find where to start working. For example if I have 1339 complex methods – I want to understand if they have something in common. and then I get to use nDepends killer feature – Code Query.

On top of the existing rules and quality checks, nDepend enables adding more checks using Linq like syntax. This helps me check huge codebases easily.

One day while analyzing a another problem I’ve noticed that the the last five classes I saw all inherited the same base class. I quickly run nDepend on the code (ok, it took a while) and then wrote this simple query:

from t in JustMyCode.Types
where t.IsClass && t.DerivedTypes.Count() > 100
orderby t.DerivedTypes.Count() descending
select new{t, t.DerivedTypes}

And found more than 20 classes that were inherited by a few hundred classes each. This simple query – written in less than 10 minutes helped me find a bug maintainability issues and better understand the client code – so I can make it more robust.

The reason it was so easy to write that query is that the syntax (called CQLinq) is readable and nDepend provides all you would expect from a full fledged IDE – intellisense, code completion and syntax highlighting.

Conclusion

Understanding someone else’s code can be an overwhelming experience – but there are ways to speed up the initial phase in which you try to grasp endless lines of logic written over a decade ago (using developer’s tears).

But don’t take my word for it – download it to your machine (no installation needed) and run it on the code you’re working on right now – you’d be amazed with the things you’ll find.

 

3 thoughts on “Using nDepend to understand, and improve legacy code

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.