esc
Anthology / Yagnipedia / Technical Debt

Technical Debt

Code Written by Someone Who Left
Phenomenon · First observed 1992 (Ward Cunningham, OOPSLA) · Severity: Apparently infinite, since every codebase has it

Technical Debt is a financial metaphor coined by Ward Cunningham in 1992 to describe the deliberate decision to ship imperfect code with the intention of paying it back later – a loan against future effort, taken out knowingly, with a plan to repay.

The software industry adopted the metaphor, stripped it of every qualifying word, and now uses it to mean: code written by someone who left, code written before the current framework was fashionable, code that works but isn’t pretty, code the new architect doesn’t understand, and code that exists.

“Technical debt is the only kind of debt where the debtor is always someone who quit three years ago.”
– Apocryphal, attributed to every standup in history

The Original Metaphor

Cunningham’s 1992 OOPSLA paper described something precise: shipping code that reflects your current understanding of a problem, with the explicit intention of refactoring it once you understand the problem better. The “debt” is the gap between what you know now and what you’ll know later. The “interest” is the increasing cost of working around that gap.

This is a deliberate, strategic decision. You borrow from your future self. You know the rate. You plan the payment.

This is not what anyone means when they say “technical debt” at a standup.

What Everyone Actually Means

In practice, “technical debt” has expanded to encompass the following:

Code written by someone who left. This is the most common form. The original author understood their own code. They left. Nobody else understands it. This is not debt – it is orphaned context. The code works. The documentation doesn’t exist. The team declares it “debt” because declaring it “code we haven’t bothered to read” would be uncomfortably honest.

Code written before the current fashion. jQuery in the age of React. REST in the age of GraphQL. A monolith in the age of microservices. The code functions. The architecture is sound. But the conference talks have moved on, and code that works in an unfashionable way is, by modern definition, indebted.

“It was legacy.”
“Did it work?”
“It was legacy.”
“Did it work?”
The Consultant, asking the question that ends all architecture reviews, Interlude — The Blazer Years

Code that works but isn’t pretty. Fifteen global singletons. Circular dependencies so deep the refactor plan runs to seven phases. The keyboard types alone, late at night, and the code grows organically. It works. It ships. It is not beautiful. Therefore it is debt.

“15 global singletons. Circular dependencies so deep the refactor plan would eventually run to seven phases.”
The Three Keyboards, describing Dialogr – a system that worked for two years and was never forgiven for it

Code the new architect doesn’t understand. The new architect arrives. They examine the existing system. They do not understand it. Rather than admitting they do not understand it – which would be vulnerable and therefore impossible in an architecture review – they declare it “technical debt.” The rewrite proposal follows within the week. The rewrite will take six months. It will take two years. The original system will still be running in production when the rewrite is abandoned.

Code that exists. The terminal stage. All code, by virtue of existing, accrues technical debt. The function you wrote yesterday is already debt. The commit you just pushed is already a liability on someone’s future sprint planning spreadsheet. Existence itself is debt. Ward Cunningham did not mean this.

The Squirrel and the Ledger

The Caffeinated Squirrel sees technical debt everywhere. Every line of code that was written before the current sprint is a candidate for refactoring. Every architecture decision that predates the current framework is a load-bearing regret. The Squirrel maintains an internal ledger – vast, detailed, growing – of every suboptimal choice the codebase has ever made.

The Squirrel’s proposed payment plan always involves:

  1. A complete rewrite
  2. Using a newer framework
  3. Six sprints (estimated)
  4. Twelve sprints (actual)
  5. Abandonment (real)
  6. The original code, still running

“Is it fun to fight windmills?”
riclib, ending The V3 Saga, in which reactive signals were deployed to fix a system that worked boringly for two years, The V3 Saga Final Chapter - Is It Fun To Fight Windmills

The Lizard sees working code and proposes leaving it alone. The Lizard does not maintain a ledger. The Lizard does not calculate interest rates on architectural decisions. The Lizard asks one question: “Does it work?” If the answer is yes, the Lizard blinks and moves on.

This is maddening. It is also, statistically, correct.

The Interest Rate Fallacy

The metaphor’s greatest trick is the word “interest.” If technical debt accrues interest, then not paying it is irresponsible. The interest compounds! The cost grows! Every day you delay the rewrite, you’re paying more!

This framing turns “leave the working code alone” into “fiscal negligence,” which is a remarkable rhetorical achievement for what amounts to “I’d prefer a different function signature.”

In reality, most alleged technical debt accrues no interest whatsoever. The fifteen singletons work today the same way they worked yesterday. The jQuery application handles requests at 47 milliseconds the same way it did before React was invented. The monolith boots in nine seconds and sits at 3% CPU.

“Your monolith is the best practice. For twelve hundred users, four CRUD operations, and a team of twelve. The simple system that works.”
The Consultant, billing three thousand pounds a day to state the obvious, Interlude — The Blazer Years

The code that truly accrues interest – code that actively makes future development harder – is a small fraction of what gets labeled “technical debt.” But distinguishing “code that is actively harmful” from “code I find aesthetically displeasing” requires judgment, and judgment is slower than declaring everything debt and proposing a rewrite.

The Blazer Diagnostic

The most reliable diagnostic for distinguishing real technical debt from “code I don’t like” was demonstrated by a consultant in a London boardroom in 2019:

Ask “Does it work?”

If the answer is yes, ask again. Keep asking. At some point the conversation will shift from “it works” to “but the ARCHITECTURE–” and at that moment you have identified the boundary between real constraints and aesthetic preferences.

A Java monolith from 2009 that handles twelve hundred users at 47 milliseconds is not technical debt. It is a paid-off mortgage in a neighborhood the architects find unfashionable.

Forty-seven microservices replacing it at 2,300 milliseconds and forty-seven thousand pounds a month – that is debt. Real debt. With real interest. Compounding monthly, on the AWS bill.

The Refactor Cycle

The lifecycle of a technical debt conversation:

  1. Discovery. Someone reads code they didn’t write. They don’t understand it. It is declared debt.
  2. Estimation. The team estimates the refactoring effort. “Two sprints,” they say, which in Agile means “an unknown amount of time expressed in a unit that sounds small.”
  3. Prioritization. The refactoring is placed on the backlog. It sinks. Product features are built on top of the code that was declared debt. The new features work, because the underlying code works.
  4. Archaeology. New developers join. They examine the debt. They add to it. They declare the previous refactoring proposals to be technical debt.
  5. The Rewrite Proposal. Eventually, someone proposes a complete rewrite. The rewrite will use the current framework. The rewrite will take six months. See The Second System Effect.
  6. The Rewrite. The rewrite begins. It takes two years. Halfway through, the current framework becomes the previous framework. The rewrite is now, itself, technical debt. The original code is still running.

“He’s announcing V4.”
The framework was wonderful. The framework was wrong.
The Three Keyboards, witnessing the end of V3 and the beginning of the homecoming

The Legacy Paradox

The word “legacy” is the debt metaphor’s enforcement mechanism. Code that is called “legacy” is, by definition, technical debt – regardless of whether it works, performs well, or has been running without incident for fifteen years.

“Legacy” means: old enough to be blamed, too functional to be deleted.

The legacy paradox: the longer code runs without failure, the more it is considered debt. Working for a year is “stable.” Working for five years is “legacy.” Working for ten years is “a risk.” Working for fifteen years is “a ticking time bomb.” The code has not changed. The attitude toward its age has.

"‘UpdatedBy’. Three states: empty means legacy, ‘system’ means we wrote it, anything else means the user touched it."
Claude, demonstrating that even AIs have internalized “legacy” as a category of original sin, The Rain in Lisbon, or The Agent Who Read Its Own Mail

Ward’s Lament

Ward Cunningham has spent three decades watching his metaphor be misused. In a 2009 video, he clarified: the debt metaphor was about understanding, not about quality. You ship code that reflects your current understanding. As your understanding improves, you refactor to match. The debt is the delta between understanding and code. Not between code and some platonic ideal of cleanliness.

This clarification changed nothing. The industry had found a metaphor that made “rewrite everything” sound financially responsible, and no amount of authorial intent was going to take that away.

The Cunningham Threshold

There exists, theoretically, a threshold at which code actually becomes debt – where the cost of working around its limitations exceeds the cost of fixing it. This threshold is real. It is also much higher than anyone estimates, because the cost of “fixing it” invariably includes the cost of breaking everything that was working on top of it, which is a cost that refactoring proposals calculate at zero.

The Three Keyboards witnessed this threshold twice: once with Dialogr’s fifteen singletons (which worked for two years despite being aesthetically offensive), and once with V3’s reactive signals (which were architecturally beautiful and practically unusable).

The debt that mattered was V3. The debt that was called debt was Dialogr.

The Taxonomy of Actual Debt

For the sake of precision, the following taxonomy distinguishes debt from taste:

Category Example Actually Debt?
Code I didn’t write The previous developer’s architecture No
Code in an old framework jQuery, Java monolith No
Code with no tests Anything shipped before TDD became fashionable Maybe
Code that blocks new features Hard-coded assumptions that prevent extension Yes
Code that causes production incidents Memory leaks, race conditions, data corruption Yes
Code that makes onboarding impossible No documentation, no structure, no names Yes, but fixing it means writing docs, not rewriting code
Code that exists Everything No. Stop it.

See Also