Compute and Manage the Technical Debt with NDepend
2 short videos that explains Technical-Debt with NDepend
Nowadays, the technical-debt metaphor has been widely adopted by the software industry. It was coined by Ward Cunningham in 1992.
This reference article by Martin Fowler describes the technical-debt metaphor in great detail. To quote M.Fowler:
In this metaphor, doing things the quick and dirty way sets us up with a technical debt, which is similar to a financial debt. Like a financial debt, the technical debt incurs interest payments, which come in the form of the extra effort that we have to do in future development because of the quick and dirty design choice. We can choose to continue paying the interest, or we can pay down the principal by refactoring the quick and dirty design into the better design. Although it costs to pay down the principal, we gain by reduced interest payments in the future.
Using NDepend, code rules can be written through C# LINQ queries. Applied on a code base a rule yields issues. A dedicated debt API is proposed to estimate both the technical-debt and the annual-interest of the issue through formulas written in C#. Both the technical-debt and annual-interest of an issue are measured in man-time.
warnif count > 0
In this example, the rule matches methods which are too complex, the complexity being measured through the Cyclomatic Complexity code metric. We can see that:
Leaving a complex method both unrefactored and uncovered by tests is an error-prone situation. At best such a situation impairs maintainability of the code, at worse it ends up in bugs at production time. The annual-interest estimates the average cost per year if the complex method is left unrefactored. This worsens if the method also is uncovered by tests. The word average is highlighted here due to the fact that, for example, out of 8 complex and untested methods maybe only one has a bug that will cost 2 days of man-work (2x8 hours) to be discovered, investigated, fixed and delivered.
Each rule in the set of default rules contains formulas to compute the technical-debt and the annual-interest for each issue. Rules and formulas can be created and customized to better match your teams' needs and habits since it is only raw C# which can be edited in Visual Studio. The main advantage is that the technical-debt estimation is entirely transparent and easily customizable with NDepend.
Annual-Interest and Severity
The annual-interest is a measure of an issue severity. The severity and the annual-interest represent the same concept where the annual-interest is a continuous measure while the severity is a discrete measure.
NDepend defines 5 levels of severity, and the severity of an issue is estimated through thresholds based on the annual-interest.
Notice that the notion of critical issue is different from the notion of critical rule. The severity of an issue is not related to its rule being critical or not. A rule can be tagged as critical to enforce some constraint on it, like for example a quality gate that fails upon critical rule violation can be written.
Technical-debt computation and results can be fine-tuned through the settings in the panel NDepend > Project Properties > Issues and Debt.
You can see:
SQALE Debt Ratio and Debt Rating
The SQALE method (commonly pronounced “scale”) is a standardized way to assess the technical-debt. NDepend implements the Debt Ratio and the Debt Rating that are part of the SQALE method.
The Debt Ratio on a code base, or on a code element, is expressed in percentage of the estimated technical-debt, compared to the estimated effort it would take to rewrite the code element from scratch. The estimated effort it would take to rewrite the code element from scratch is inferred from the code element size in lines of code, and from the debt setting named Estimated number of man-days to develop 1.000 logical lines of code (see the screenshot in the previous section about debt settings).
The value of the Estimated number of man-days to develop 1.000 logical lines of code setting is just an estimation so in the short-term it is meaningless. After a few man-months or even man-years of development this value is typically stable enough to rely on for estimation purposes. This estimated setting also needs to take into account the cost of writing unit-tests. The default value is 18 man-days which represents an average of 55 new logical lines of code, 100% covered by unit-tests, written per day, per developer.
The Debt Rating of a code base or of a code element is inferred from thresholds applied on the Debt Ratio. The Debt Rating is in the range A, B, C, D, E. The four thresholds are customizable in the debt settings panel (see the screenshot in the previous section about debt settings). The default thresholds are:
The code base Debt Rating and Debt Ratio values are shown in the Dashboard. In the section Browsing the Technical-Debt we'll show that simple C# code queries can display the Debt Ratio and Rating values for any code element.
Prioritizing issues fix and the Breaking-Point metric
The Breaking-Point of an issue or of a set of issues, is the time point from now to when the estimated cost-to-fix the issue(s) will reach the estimated cost to leave the issue(s) unfixed.
The breaking point is the debt divided by the annual-interest. For example if the estimated cost-to-fix the debt is equal to 10 man-days and the estimated annual-interest is equal to 2 man-days per year, then the breaking point is equal to 5 years from now.
Notice that a breaking point which is lower than a year means that during the next 12 months, it is estimated that it would be cheaper to fix the debt than not to fix it.
Notice also that a breaking point is not measured through man-time like debt or annual-interest (a man-month or a man-year), but rather through regular duration (months or years). Breaking point values are typed with TimeSpan.
When it comes to prioritizing issues to fix first, the issue severity is an important parameter. As a reminder: the severity is the discrete measure of the annual-interest. Hence the higher the annual-interest, the more important it is to fix.
However, given a certain severity level, not all issues are equal. Some will demand more effort to fix. This is estimated through the technical-debt measure. Hence, to estimate the Return On Investment (ROI) of an issue fix, it makes sense to estimate the debt divided by the annual-interest. This estimation is the breaking-point for which the lower the value, the higher the ROI.
Let's specify that in the set of default rules, issues that are relative to new problems since the baseline, such as API breaking changes, code elements quality getting even worse, new code elements not tested... are issues which produce a higher annual-interest and thus a higher severity than the other issues. This complies with the best practice to fix recently introduced issues first.
Browsing the Technical-Debt
In the introduction we saw that code rules are implemented through C# LINQ queries and we also saw that the debt and annual-interest estimations are inferred from formulas embedded in these LINQ queries.
This C# LINQ queries scheme goes further and can be used to browse and explore the technical-debt. The domain Issues is an enumerable of all issues found in the code base. Obviously, queries that rely on this domain are executed after all rules have been executed.
A complete presentation of CQLinq details concerning Issues, Rules and QualityGates browsing can be found here. Below we'll focus on how to generate automatically CQLinq queries to browse the issues-set.
For example, when clicking a number of issues on the dashboard, like new major issues since baseline in the example below, a code query is generated to list relevant issues. Notice that the issues can be grouped per rules or per code elements. In the screenshot below issues are grouped per rule.
Notice the Explore Debt menu on the Dashboard that generate some queries on the rules, issues and code elements to explore in-depth the technical debt.
Right-clicking a Rules category, like the Code Coverage category here, shows menus to query issues in this category:
Some default debt and issues queries can be found in the Hot Spots group. For example the query Types Hot Spots lists the types with most debt first.
The Rules domain is an enumerable of all active rules. It lists both violated and non-violated rules. Queries can be written to list rules per debt and number of issues. Matched rules can be grouped through categories.
With no surprise, coverage, code quality and architecture are categories that will often generate the most debt and issues.
The baseline plays a major role when it comes to exploring the issues set because new or fixed issues since the baseline assess the quality of recent work.
Per default the baseline is the historic analysis result closest to 30 days ago and per default, a historic analysis result is persisted at most every day.
Because when assessing recent work quality, one will certainly want to juggle between yesterday, last week and last month baselines, the NDepend dashboard allows you to apply a temporary baseline with a single click. The debt and issues set is then recomputed within a few seconds accordingly.
And since assessing issues and debt since the baseline is important as we just saw, all Hot Spots default queries come with a since baseline version. For example here is a query to assess New Debt and Issues per Rule since the baseline.
Let's mention a subtlety when it comes to debt and issues querying. Types contain methods and fields, namespaces contain types and assemblies contain namespaces. Hence types, namespaces and assemblies are code element parents.
All issues-related ICodeElement extension methods like elem.Debt(), elem.AnnualInterest(), elem.Issues(), have a version prefixed with All that returns the debt and issues for the code element parent and all its child elements. Hence:
Technical Debt and Quality Gate
You'll find default quality gates relative to technical debt and issues, including Percentage of Debt, New Debt since Baseline or New Blocker / Critical / Major Issues. Quality gates relative to absolute technical debt value are disabled by default because the proper thresholds can only be defined in the context of a particular project.
The same way Issues and Rules are predefined as queryable domains that provide an enumerable of issues or rules, the domain QualityGates is an enumerable of quality gates. The default query below estimates the quality gates trend since the baseline. Notice that quality gates that rely on the baseline (like New Debt since Baseline) have neither a value nor a status defined on the baseline.
Reasons why Technical Debt might be zero or incomplete