In a recent project, I am faced with a challenge to understand a large .NET code base developed by several team members over quite a few years. All of them had left, leaving behind only a couple documents covering the usage of the software; very little documentation is found on the program architecture or algorithms. Comments in the code are also scarce.
The total code base is tens of thousands of lines long, so I direly need a way to understand the architecture quickly.
Being a visual person, I started with Visual Studio's class diagram. That helped a little but not too much, because the class diagram only revealed data structures. What's more important to me is the call diagram among functions which can show me the flow of computation in a bird's-eye view. I heard somewhere there is such a tool in Visual Studio Enterprise, but I only have the free Visual Studio Community and wouldn't be able to afford an Enterprise license.
I tried creating function call diagrams myself using drawing programs like Visio, but that soon proved infeasible. So I searched online and found NDepend. After reading abundant reviews, I decided to dive in. I downloaded the trial, installed it, and created an analysis for my project. Then, following the online help, I created a Callers / Callees graph using the menu as shown in Figure 1.
At first, I do not understand why I have to base my selection on one method first. Why don't we just graph everything? Then I realized two things:
- NDepend uses LINQ queries to select the elements to show, so there must be some defined starting point.
- For a large project, showing everything is not helpful. Example at hand: when I tried generating a graph based on a top level function and chose "... that I Use (Directly or Indirectly)", I got the spaghetti ball in Figure 2.
After a while, I got the hang of it. I have to be intelligent in choosing one of the 4 options: "... that are Using Me Directly", "... that are Using Me (Directly or Indirectly)", "... that I Use Directly", and "... that I Use (Directly or Indirectly)". The purpose is to reveal enough structure while avoiding showing too much at one time. So I was able to generate useful graphs like the one in Figure 3. On the bottom in orange is the function I am inspecting; the bright green color nicely shows functions who directly call this function; in darker green color are those functions who call indirectly. It's easy to spot points of interest: many calls converge onto a function; one function calls many other functions; or a single point where all paths pass. Last but not the least, the box size is correlated to the number of lines of code inside the function, so you can find the elephant in the room with just one glimpse.
With the help of NDepend graphs, I was able to quickly develop some sense of direction in the large code base. This does not replace reading the code in detail and/or stepping through the execution. But it does provide a series of powerful maps that you can rely on to navigate the code base and narrow down to what you really want to examine first. Just for this reason, NDepend will be indispensable to my .NET development, not to say there are so many other great features to be explored. Heck, I may even start tracking the technical "debt" and try to repay some in the future.
- 7th December, 2018: Initial version