Technical debt – analogous to financial debt – is a useful way for managers and engineers to think rationally about when to overhaul something and when to leave “well enough” alone and about prioritizing resources when managing a complex design.
But a number of challenges remain. Can we reliably and repeatedly measure technical debt in different parts? Can we measure the cost of servicing that debt? What kind of product-related complexity metrics correlate with significant maintenance costs? Can we use them as one way of measuring our technical debt?
To begin to answer these questions, you can categorize technical debt into one of three types. Each type has associated metrics that are useful for measuring, and ultimately managing, the costs:
1. Code Debt
One of the goals of good code is to achieve maximum functionality with a minimum volume of lines of code. Code bloat, complexity of functions, and other measures of complexity contribute to code debt.
Code bloat occurs when functions are copied and pasted. This creates volume without adding value. Instead of tweaking original code until it solves both original needs and new needs, coders make the mistake of copying and pasting something that is close to what they want. This is easier in the short term but contributes to the proliferation of bugs in the long term. Writing a function that is called into takes more effort but is cleaner overall and reduces code bloat.
Complexity of Individual Functions
Individual parts of code can be overly complex. One measure of the complexity of a function is the McCabe Metric. McCabe proposed a “cyclomatic” complexity metric. It assigns a number to a “structured program” or block of executable code based on a static analysis of the number linearly independent execution paths that can be followed as a program executes. In modern programming languages, McCabe scores typically apply to procedures (called functions in C) or class methods. Alternative paths through a procedure result from conditional branching statements (if statement, switch/case statement, while loops, etc.).
A high McCabe score indicates a function that needs to be reworked so it is less complex. McCabe is a well-validated measure. To learn more about McCabe Metric, visit our resources page.
Other Measures of Complexity
Sonar is an open source software quality management tool dedicated to continuously analyzing and measuring source code quality. Sonar uses the SQALE Method (Software Quality Assessment based on Lifecycle Expectations). Developed by inspearit France (formerly DNV ITGS France), it is used by many organizations for applications of any type and any size. This method is implemented by several static code analysis tools that produce the defined indices and indicators. In addition, this method allows doing the precise management of design debt for Agile software development projects.
2. Architectural Debt
Architectural debt refers to code quality at the macro or system level. Architecturally complex regions in a system have fewer architecture patterns mediating the relationships between system elements. System architects use patterns to make individual parts understandable, even though the system as a while is beyond the bounds of human understanding. Modularity, hierarchy, layers and reuse are four common means to alleviate or prevent architectural debt.
Modularity incorporates high cohesion, low coupling, private internals, defined interfaces, and “small enough” components. The benefits of modularity include the ability to collect similar functionality in one location; to prevent changes in one area from impacting others; to bound cognitive burden placed on individuals; to bound communication requirements; and to make the system more evolvable.
Hierarchy incorporates explicit dependencies between modules. The dependencies are non-circular. The benefits of hierarchy include that it facilitates bottom-up/top-down design; that complex problems are broken into small simple ones; that the code is infinitely scalable and can grow without increasing local complexity; and that it prevents feedback in the development process.
Layers are rigid and defined interfaces that partition the system. There is interaction between layers without skipping. The benefits of layers include the ability to create powerful concepts and abstractions. In addition, layers make the overall system more flexible; they insulate people working in different domains; and they can hide information.
Reuse refers to shared utilities that are widely used. Reuse enables the coder to separate application-specific details from useful underlying abstract structure. The benefits of reuse include long-term waste reduction, the ability to build on and help harden mature capabilities. In addition, many eyeballs often detect defects faster and new efforts move along more quickly.
These patterns make it easier to enhance and maintain large engineered systems. When they are absent, the organization suffers. There are several means to measure and begin to address architectural debt. These include the use of Design System Matrices (DSMs) to graph the dependencies between source code files. Another means is the classification of each file as peripheral, utility, control or core based on its reachability within the network.
For more information on methods for examining software systems and classifying architectural complexity, see the work of Carliss Baldwin and Alan MacCormack on our Resources page.
3. Ancillary Debt
Ancillary debt refers to quality in other areas such as test coverage, documentation, usability or security. Each of these contributes to technical debt over time.
The lack of test coverage encourages quick and risky short-term fixes to correct bugs. A lack of documentation leads to a situation in which code is created without supporting explanation. The work required to write documentation is a debt that must be paid. Poor usability must be corrected at a future date. And security problems are another contributor to unpaid technical debt. The “interest payments” on these short-comings include both the necessary maintenance and the absence of maintenance by other coders in the system. The buildup of these types of debt is a major contributor to missing deadlines, cost overruns and, more seriously, system failures.