As a consultant, I often need to work with the code that I do not know. I need to understand design decisions that were not mine. I need to get acquainted with architectures I did not help to conceive. Static analysis tools, like NDepend and Visual Studio Code Map, help me do my job more effectively and efficiently.
Today, I am starting a series where I will share how Static Analysis tools are helping me to produce better software. In this post, I will share with you my first steps when getting in touch with some unfamiliar code base.
STEP #1: Recognize the components and their relationships
As an example, let’s assume that I need to start to work with the Microsoft’s eShopContainer project.
Just after downloading the code and get it up and running, my first step would be to generate a Code Map from the solution (using Visual Studio).
This simple view allows me to get a first understanding of the software components and their relationships. By default, Visual Studio uses the solution’s folder structure to group the projects.
Note that there are no “explicit dependencies” between the web apps and the services. Also, there are no “explicit dependencies” between the services as well. The reason why this is happening is that the connection only exists in run-time, via HTTP/messaging (which is good, considering this as a Microservices architecture implementation). But it requires me to talk with people to understand the business.
This view also helps me to discover some “technical complexities” added to the project. For example, there are at least two implementations of EventBus. Is that needed? What are the business motivations to this? In the “real world” would exist two “release” strategies, two support strategies, … (I know this is a sample project, but, …)
STEP #2: Discover the size of each component
My second step would be to generate a Dependency Graph with the box sizes representing the number of lines of code (using NDepend).
Now I have a good idea of the proportional size of the projects. Also, I can estimate the amount of time I would need to read the code (It’s not precise, but generally it is good enough).
This graph has been helping me to identify disproportional sizes in the components of the applications. Disproportionally large assemblies are a good indication of poor architecture/design choices. For example, it is strange to have a “so big” identity project. Also, it is strange that the Ordering API is twice more prominent than the Ordering Domain and Ordering Infrastructure. Would be the API doing more than it should?
In the “real world,” I have seen “big” assemblies as a symptom of coupling and difficulties to meet the business needs.
STEP #3: Discover the core (hard to change) components
My third step would be to generate the Dependency Graph, this time using the afferent coupling to define the box sizes.
Analyzing the resulting graph, I know that the EventBus is a core component and concept in this project. Also, I am aware that changing EventBus is dangerous because it can affect all the system.
Also, at this moment, I start to think and ask about the “choreography” of the services. It seems that there are no “Orchestrators” here. Would I be right?
It is important to remember that this graph does not include “implicit dependencies.” [tweet]When analyzing code, no tool will do all your job. Charts, reports, matrices will only help you to be more precise and to save (some) time.[/tweet] You will still need to talk with the domain people.
STEP #4: Get a better (detailed) understanding about the relationships
My fourth step would be to generate a Dependency Matix.
The blue cells indicate that the assembly in the column is using the assembly in the row. The green cells suggest that the assembly in the row uses the assembly in the column.
This matrix allows me to see that all the web projects are using the “Microsoft.AspNetCore.HealChecks” package. Nice, huh?!
In the real-world, I have seen assemblies with thousands of dependencies to another one.
STEP #5: Create a list of critical issues
Finally (for a while), I use NDepend to check critical potential problems in the code.
NDepend uses generic rules to try to identify pain points in the code. It is usually really efficient.
The “avoid methods too big …” rule is the more commonly violated one.
In this post, What I recommend you do when getting in touch with unfamiliar code using proper tools. Next post, I will start sharing some techniques I use to dig deep into the code discovering and removing pain points.
Could I help you? Let me know.