← All writing
Philosophy · · 7 min

Technical debt is a bad metaphor

On what the phrase originally meant, four ways the comparison doesn't hold, and the fact that a metaphor decides which questions seem sensible before anybody asks them.

Culture

“Roughly how much technical debt are we carrying?”

Perfectly reasonable question, asked in good faith, by somebody who needed to plan a budget. And the four of us in that meeting produced about 90 seconds of hedging, because there is no answer, and because saying “that question doesn’t quite mean anything” sounds evasive.

I’ve been chewing on it since, and I’ve concluded the problem was in the question, and the question was made available by the metaphor.

What it originally meant

The phrase comes from Ward Cunningham, in the early nineties, and his version was much narrower and considerably more interesting than what we do with it now.

His point was about understanding. You start building something before you fully understand the domain, because building is how you come to understand it. So the first version reflects an incomplete model. That’s fine, and shipping it lets you learn faster than thinking about it would.

The debt is the gap between what the code says and what you now know. The interest is that every subsequent change is harder while the gap persists, because you’re working against a model you’ve outgrown. And you repay it by refactoring the code to match your improved understanding.

He was clear, later and repeatedly, that this was never a license to write bad code and call it borrowing. Deliberately shipping a mess and labeling it debt is, in his framing, not the metaphor at all.

Which is now approximately the only way anybody uses it.

Four ways it doesn’t hold

Real debt has a principal. You can say you owe $11,000. Nobody can say how much technical debt they have, and every attempt to produce a figure, whether in days or points or a score out of a hundred, is an invented number that will be treated as a measurement the moment it’s on a slide.

Real debt has an interest rate you agreed to. You signed something with a number on it. The cost of messy code is unknown, variable, and frequently zero. Code nobody touches costs nothing, forever. A tangled module that hasn’t been opened since 2019 is not accruing anything, and it never will unless somebody opens it. That’s a completely different financial instrument from a loan.

Real debt is repayable in a fungible currency. You can pay off a loan with money from anywhere: a bonus, a sale, a gift. This can only be repaid with engineering time, from people who understand the specific code, which is the exact resource you’re short of. There is no equivalent of borrowing from a cheaper lender.

Real debt is voluntary. Somebody decided to borrow. But most of what gets called technical debt was never a decision. The requirements changed underneath the code. The framework moved. The API was deprecated. The person who understood it left. That isn’t borrowing, it’s drift, and the difference matters because a metaphor about borrowing implies a borrower.

A metaphor about borrowing implies somebody borrowed. The borrower is always whoever was here before, and they're not in the room to answer.

And a fifth, which I think is the most damaging: repaying real debt is always good. Repaying technical debt is frequently a waste. Time spent tidying a module you’re about to delete, or one nobody will ever open again, is not virtue. It’s work with no return, undertaken because a word implied it was owed.

Why we keep it anyway

I should be fair, because the metaphor has done real good and I’ve used it myself this year.

It’s legible to people who control budgets. “We’re carrying technical debt and we need to pay some of it down” is a sentence a finance director understands immediately, maps onto a category they already think about, and can approve. Nothing else engineers have come up with has ever landed like that.

Before it existed, the pitch was “the code is messy and we’d like some time,” which sounds like craftsmanship, which sounds like preference, which loses to a feature every time. The debt framing has bought a great many people a great many necessary weeks and I’m not going to sneer at it.

So it’s a political instrument that works. It’s just also a description that misleads, and I don’t think anybody has noticed how much the second cost is compounding.

What it costs

Two things.

It makes everything one category. An unpatched runtime with a known vulnerability, a component with confusing naming, and a data model that no longer fits the business are three completely different problems with different urgencies, different consequences and different arguments. Filed under one heading, they get prioritized against each other by feel, and the one that gets picked is the one that annoys the loudest developer.

And it invites the wrong shape of response. A debt metaphor suggests periodic repayment: a cleanup sprint, a quarterly allowance, 10% of every cycle. And that’s precisely the wrong shape, because the value of fixing something is proportional to how often somebody has to touch it, not to how bad it is. A cleanup sprint tends to produce work on whatever is most visibly ugly, which is uncorrelated with what’s actually costing anybody time.

Three words that do the job

What I’ve been trying instead, and it’s three separate words because they’re three separate things.

Friction, for the everyday case, measured where it occurs. Not “we have debt in the checkout” but “changes to the checkout take about three times as long as changes anywhere else, and here’s why.” That’s a claim about a specific place, it can be checked, it points at what to fix first, and it can be tracked over time to see whether the fixing worked.

Risk, for the security and end-of-life category, which is not debt at all and is an exposure with a date attached. “The runtime stops receiving security patches in 14 months” is not a thing you owe, it’s a thing that will happen to you, and it gets a completely different conversation and a different budget.

Unfinished, for the honest one. This wasn’t done. We knew, we shipped anyway because of the launch date, and here’s the list. That’s not a loan, it’s an incomplete piece of work, and describing it plainly is much more likely to get it finished than describing it as a financial abstraction.

Three words, three different meetings, three different people who need to care. The single word collapses them and the collapse is the problem.

Metaphors decide the questions

The bit I actually find interesting, and the reason this is a longer post than the subject strictly deserves.

A metaphor doesn’t just describe a thing. It determines which questions about that thing seem sensible to ask, and it does that before anybody notices they’ve adopted it.

“How much do we have” is an obvious question about debt. It’s a nonsense question about code, and the metaphor is what makes it feel obvious. So a room full of competent people spends 90 seconds failing to answer an unanswerable question, and the answerable one, which is “where does it hurt and how often,” never comes up, because it isn’t the question the metaphor suggests.

That’s a general property and it’s worth watching for elsewhere. The metaphors we use for our work do a lot of quiet governing. Building a website is not construction, a codebase is not a garden, a team is not a family, and each of those comparisons imports a set of expectations that nobody argued for.

What I said in the meeting

Eventually: I can’t give you a number, and I can tell you that changes to the ordering flow take about three times as long as changes anywhere else, and I know why, and it’s about six weeks to fix.

They approved four of them. Which is a better outcome than any number I could have made up, and it took the same amount of meeting.

Read similar posts
6 min

What an RSS feed is for

Somebody who has been building websites for two years asked me what the orange icon in the footer did, and I gave an answer that was mostly about 2007, which was no use to them at all.

6 min

The blogroll

I found a writer I now read constantly because of a link at the bottom of somebody else's post, and it took me a day to work out why that felt unusual, which is that almost nothing reaches me that way any more.