On Exactitude in Technical Debt

If software is such stuff as dreams are made on, how do we talk about nightmares? Software is not the tangible, kickable stuff our senses are tuned to, so we draw on metaphor to communicate and reason about it.

The 1970s offered up spaghetti code to describe the tangle of unstructured control flow. This has inspired many software-as-pasta descriptions, from lasagne for layered architectures to ravioli for—pick a decade—objects, components, modules, services, and microservices. Beyond its disordered arrangement, however, spaghetti has little to offer us as a metaphor. It doesn’t provide us with a useful mental model for talking about code, and has far too many positive associations. If you love both ravioli and spaghetti, it’s not obvious that one of these is worse for your software architecture than the other.

A metaphor is a mapping that we use to describe one thing in terms of another—sometimes because we want to show something familiar from an unfamiliar angle, as in poetry, but sometimes because we want to show something unfamiliar or abstract in a more familiar light, as in software. To be considered good, a metaphor has to offer a number of points of useful correspondence with what is being described. Pasta doesn’t quite do this.

Another quality of a good metaphor is that it should not have too many obvious points of conflict. It will never map its target perfectly—a metaphor is a conceit not an identity—but a good metaphor is one whose key qualities don’t contradict the very thing we are trying to say, whose points of difference don’t distract from the mental model being shared.

We sometimes talk about code decay and software rot. These terms give a sense of degradation over time. This seems accurate and relatable. They also suggest a response: cleaning (we brush our teeth to reduce the chance of tooth decay) or treatment (we treat wood to avoid it rotting). So far so good… but the problem with these metaphors is they refer to natural processes that happen independently of anything we do. If you don’t brush your teeth, you will experience decay. If you don’t touch code, it doesn’t intrinsically degrade.

The third quality of a metaphor that makes it effective is familiarity to its audience. Explaining something unfamiliar in terms of something else that is also unfamiliar can be a long road to travel a short distance (or to end up where you started). If you are familiar with the concept of entropy in statistical mechanics, with the second law of thermodynamics, and with the idea that work is needed to reduce entropy and increase order in a system, then software entropy might strike you as a descriptive metaphor—and not simply because the word work transfers happily from the world of thermodynamics to the day-to-day experience of developers. If, however, these concepts are not accessible and require explanation, then, regardless of its other merits, software entropy may not be the best way to talk about accidental complexity in code.

Perhaps the most popular metaphor in use is based on financial debt, originating with Ward Cunningham in 1992. As Martin Fowler described in 2003:

Technical Debt is a wonderful metaphor developed by Ward Cunningham to help us think about this problem. 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.

When we look at technical debt, we see a metaphor that checks all three boxes: it has a number of useful points of correspondence; the points of difference don’t overwhelm the core idea; it is familiar. Furthermore, it brings with it a useful working vocabulary. For example, consider what the following debt-related words suggest to you in a software context: repayment, consolidation, creditworthiness, write-off, borrowing.

Although we know that by definition no metaphor is perfect, there are two common ways in which the metaphor is misapplied: assuming technical debt is necessarily something bad; equating technical debt with a financial debt value. The emphasis of the former is misaligned and the latter is a category error.

If we are relying on the common experience of our audience, financial debt is almost always thought of as a burden. If we take that together with the common experience of code quality and nudge it with leading descriptions such as “quick and dirty,” it is easy to see how in everyday use technical debt has become synonymous with poor code and poor practice. We are, however, drawing too heavily on the wrong connotation.

Rather than reckless debt, such as from gambling, we should be thinking more along the lines of prudent debt, such as a mortgage. A mortgage should be offered based on our credit history and our ability to pay and, in return, we are able to buy a house that might otherwise have been beyond our reach. Similarly, Ward’s original motivation was to highlight how debt in code can be used for competitive advantage:

Shipping first time code is like going into debt. A little debt speeds development so long as it is paid back promptly with a rewrite.

This comes with a clear caveat and implication: a debt is a loan. A debt is for repayment, not for running up:

The danger occurs when the debt is not repaid. Every minute spent on not-quite-right code counts as interest on that debt. Entire engineering organizations can be brought to a stand-still under the debt load of an unconsolidated implementation.

As in the real world, how we run up debt and how we manage it turn out to be more complex than the simplicity of our best intentions. There are teams that make time-saving decisions wisely, revisiting and addressing them later in a timely manner. But in most cases where debt is incurred, discussed, and lamented, codebases reflect the firefight of different priorities, skills, and people. It’s still technical debt, but it lacks the prudence and intention of Ward’s original purpose.

There are also teams and tools that embrace the debt metaphor so tightly that they forget it’s a metaphor. They treat it literally and numerically, converting code quality into a currency value on a spreadsheet or dashboard. The consequences of this thinko range from being a harmless fiction largely ignored by developers and managers to a more damaging numerology that, even though it’s well intentioned, can mislead development effort.

If we’re going to quantify it, what is it we’re quantifying? Do we list off code smells? What is the debt value of a code smell? Is it constant per kind of code smell? For example, is duplicate code characterised by a single cost? And are code smells independent of one another? Consider that, for example, duplication is sometimes used to reduce coupling, so the debit becomes a credit in that context. We can conclude that a code smell is not an isolated thing with a single look-up debt value, so this is clearly a more complex problem dependent on many factors. As a multivariable problem, what does it depend on? And how? And how do we know? And what would the value or—more likely—value distribution reflect? The cost of fixing? Or, more honestly, an estimate of the cost of fixing?

But even if we are somehow able to conjure a number out of this ever-growing list of considerations—and even if that number has some relation to observed reality—we have put a number to the wrong quantity. We have, in fact, missed the whole point of the metaphor.

Technical debt is not the cost of repaying the debt: it is the cost of owning the debt. These are not the same. That is the message of the technical debt metaphor: it is not simply a measure of the specific work needed to repay the debt; it is the additional time and effort added to all past, present, and future work that comes from having the debt in the first place.

By taking the metaphor literally, we have robbed it of its value. Its value is to offer us a figure of speech not of currency, a mental model for talking and reasoning about qualities of our code that are not simply stated in code. No matter how well meant, pushing any metaphor beyond its applicability leads to metaphor shear. It is, after all, metaphor and not identity.

Read more: feedproxy.google.com