The secret ingredient for code architecture evaluation

Short description

The metric of good code architecture is important as a loss of qualities such as scalability, testability, and comprehensibility can destroy a company. Uncle Bob introduces two concepts, stability and abstractness, and proposes metrics to analyze code design and identify areas of improvement. Components with a balanced ratio of abstractness and stability are optimal, as shown on the “main sequence” in a graph. The value of the D metric indicates how distant components are from the main sequence. These metrics can help evaluate code architecture and improve projects.

The secret ingredient for code architecture evaluation

Good and bad code… Sometimes we use these concepts in conversation, but not everyone will be able to formalize them. You can follow different design principles, change workflow, etc., but today I want to talk about a numerical metric that describes how good the architecture of your code is.

Why is it important to understand all those aspects, including the concept of “good” code? If our code loses such qualities as scalability, testability, comprehensibility, and others, then there is a risk of increasing the price per line of code by tens and hundreds of times, which can destroy the company. I observed this myself.

I saw the metric in question in Robert Martin Bob’s book Clean Architecture. This book is essentially a long summary of his original article, and at the same time Chapter 22)
Uncle Bob introduces 2 concepts – stability and abstractness.

Stability – This is the ability of the component to maintain its state under external influences. In the context of software development, this means how often you have to change your code due to dependency changes. The less you have to do this, the more stable your component is considered to be. To illustrate, consider an example with a web service that your application depends on. If that service decides to change its API and you have to change your app because of it, that means your app is not resilient to that change.

Abstraction is the degree to which a component uses abstractions to hide implementation details. Abstractions allow us to create components that are easier to modify and extend because they are decoupled from concrete implementations. Now that we understand what persistence and abstractness are, let’s introduce the formulas by which we will calculate these metrics.

Metric I (fragility): I = fan_out / (fan_in + fan_out)where fan_in – The number of input dependencies (how many components depend on this), a fan_out – Number of source dependencies (on how many components this component depends on). The I metric varies from 0 to 1, where 0 represents maximum resilience and 1 represents minimum resilience.

Metric A (abstractness): A = Na / Ncwhere Nc – the total number of classes in the component, a Na – Number of abstract classes or interfaces. This metric also varies from 0 to 1, where 0 means a completely concrete component and 1 a completely abstract one.

Using these metrics, we can conduct code design analysis, identify potential problems, and areas for improvement. For example, components with a high level of abstraction and low persistence can be considered well-designed because they easily adapt to change and scale. However, there are certain areas where components can cause problems.
To visualize these metrics and determine where the different components of your system are, you can plot a graph with an abstractness (A) axis and an impermanence (I) axis.

In this graph, we can answer two special areas:

  1. Pain area: In this zone, the components are very stable and concrete. This means that they are difficult to change due to the large number of dependencies and cannot be easily extended because they do not use abstractions. Such components can become a source of problems during system changes.

  2. Zone of futility: This zone contains components with high abstraction and low input dependencies. Usually this is outdated and forgotten code that is not actively used in the system.

Optimally, the components should be on the so-called “main sequence” – the line that runs from the point (0,1) to the point (1,0) on the graph. This means that they have a balanced ratio of abstractness and stability, which makes them well designed and adaptable to change.
We can plot the scatterplot of the components and see how far they are from the main sequence. In the figure below, we can see that some modules are removed by more than 2 standard deviations. This is not good.

And so we come to the basic metric proposed by Robert Martin. It allows you to assess how distant the components are from the main sequence: D = |A+I-1|. A value of 0 indicates that the component is directly on the main sequence, and a value of 1 indicates that the component is at the maximum distance from it.
We can also plot the change of the metric D over time and take certain actions in advance.

So, in summary, today we were introduced to the persistence (I), abstractness (A), and distance from the main sequence (D) metrics that can help evaluate the quality of code architecture and identify areas for improvement.

Perhaps some of you will decide to put these metrics into practice and write your own graph visualization tool in Python or another programming language. If you manage to create something interesting and useful, be sure to share your results in the comments below this article! Together we can make our code even better and more efficient 🙂

Thank you for your attention and I hope that the proposed metrics will be useful in your development. Good luck improving your projects!

Related posts