Sonargraph's Maintainability Level is one of the source-code metrics Understand computes across 17+ languages — browse them in the GUI, export and track them, or script them with the Python API.
Sonargraph's Maintainability Level
API ID: SonargraphMaintainabilityLanguages: Any
Targets: Architectures, Project
The percentage a system is maintainable based on dependencies, with higher values better.
Sonargraph defines their Maintainability Level metric in their blog article A Promising New Metric To Track Maintainability. It is calculated from a dependency graph of system components, with higher values indicating more independent components. Sonargraph recommends aiming for a value higher than 70%, and avoiding values lower than 50%.
Given a dependency graph of system components, the calculation first combines strongly connected components into a single node to get a directed acyclic graph. The graph is then split into levels.
Each node's value is 1 - (influenced_components / components_in_higher_levels).
The idea is to reflect how easily a component can be changed. The more
incoming dependencies, the harder it is to change a given component.
A component in the highest layer is not depended on by anything so it
can be easily changed (value = 1). In contrast, a component
on a lower level is less maintainable based on how much of the system depends
on it.
For a node representing multiple components (a cycle), the value is multiplied
by the number of components in the cycle. Cycles bigger than 5 have an additional
penalty of 5 / size multiplied with the node score.
The final value is the average of each node's value, multiplied by 100 to convert it to a percentage. For example, consider a system with 10 nodes: A-J. Suppose C, D, E, F, G and H form a cycle and A->C, B->I, H->J, and I->J.
Then:
- A and B on the highest layer each have no incoming dependencies. They each get the full weight of 1.
- I is in the middle layer. There are 2 higher components (A and B) and 1 of
them depends on I. So its weight is
1 - (1/2) = 0.5 - CDEFGH is also in the middle layer so there are 2 higher components (A and B), 1 of which depends on CDEFGH. So the initial weight is 0.5, like I. But there are 6 components in the node, so that value is multiplied by 6, giving 3.0. Finally, the cycle is bigger than 5, so a penalty of 5/6 is applied, giving a final value of 2.5.
- J is on the lowest level, with 9 components higher. All 9 depend on J
so its weight is
1 - (9/9) = 0
So the final value is (1 + 1 + 0.5 + 2.5 + 0) / 10 = 0.5 = 50%.
This implementation is likely to differ from Sonargraph in the following ways:
- A component is always a file. In Sonargraph, the definition of a component can vary by language. For C/C++ specifically, header and code files are combined in Sonargraph and not in Understand.
- Understand's file dependency graph is used, and likely differs from dependencies calculated by Sonargraph.
- The implementation uses the ML2 formula from the blog article, not the final ML4 formula. ML3 adds a sliding minimum for systems with fewer than 100 components. The ML4 formula also considers package cyclicity for some languages.
For a similar metric not influenced by level, see PropagationCost.