Chapter 15
Technical Debt
Deliberate debt, on the record. Payback triggers. The cost of putting things back.
On this page
Recognizing it, paying it down, living with it.
The Metaphor
Ward Cunningham coined the term.[1] Like financial debt, technical debt lets you move faster now at the cost of paying interest later: ship the quick solution and every future change to that code pays the tax.
The metaphor is useful but imperfect. Financial debt is precise; you know exactly what you owe. Technical debt is fuzzy. You don't always know you have it until it hurts.
Not all debt is bad. The problem isn't debt itself. The problem is unmanaged debt that compounds until the system becomes unmaintainable.
Deliberate Debt
The distinction that matters most is not clean versus messy. It is on the record versus invisible. Deliberate debt is a conscious choice you can manage. Accidental debt accumulates in the dark until it becomes a crisis.
Taking on debt is sometimes exactly the right call: a time-sensitive window, a discovery phase where you don't yet know what to build, throwaway code that genuinely won't ship, survival mode. What makes it strategic instead of reckless is the discipline around it:
Be explicit. Say out loud: "We are taking on debt here. This is what we're trading off."
Document it. In the code, in the ticket, in a register. Don't let it become invisible.
Name the payback trigger. Not "later." A condition: "when this module next changes," "when traffic passes X," "next quarter's first sprint." Debt without a trigger is drift with paperwork.
Actually pay it back. This is where teams fail. If your team's pattern is taking on debt and never paying it, stop taking on debt.
And the debt not worth paying: code you never touch. It costs nothing. Leave it alone.
Project Residue
Not all debt is code. Work leaves residue behind it: dead spike code, stale branches, failing tests that became background noise, notes that no longer match reality, half-finished work with no marked restart point. Each piece looks harmless. Together they consume the capacity you need for the next move.
I relearned this in a garage. After several cycles of assembling and rearranging a workshop, the space had filled with the project's own residue: shipping boxes, packaging, displaced items. The next useful step was not another assembly step. It was breaking down the boxes. Space was the constraint, and the boxes were consuming it while contributing nothing; their carrying cost was real and their future value ("what if I need to return something?") was hypothetical. When space is the constraint, unfinished cleanup is unfinished work.
That is the same calculation as the debt decision, run in reverse. Preserving optionality has a carrying cost. Keep the option when reversal is expensive and the option is cheap to hold. Discard it when holding it costs real capacity now and recovering later is cheap. (Incidents names this the cleanup mode: remove what no longer earns its space.)
The Restart Point
The stopping ritual I use now, after watching restart costs eat entire sessions:
- Remove what no longer earns its space.
- Verify the current state against the plan.
- Capture what changed while you still remember why.
- Name the next concrete action.
- Stop.
A good stopping point does not merely record where you stopped. It makes starting again cheap. In engineering terms: restore the suite to a known state, delete or isolate the disposable code, reconcile the plan with what you learned, write down the next failing test.
The Cost of Putting It Back
When the tools needed permanent homes, the question looked like "where does everything fit?" It wasn't. What I did not know was which arrangements would survive repeated real use. A wall of neatly hung tools is easy to design and satisfying to photograph, and the actual decision was between that visual neatness and what retrieval and return would cost in the middle of real work. Every time a tool goes back to its home, someone pays that cost. If it is too high, the returns stop happening and the system decays, no matter how clean it looked on day one. So the tool chest went near the hot path, on the route my hand already travels, and the layout was judged by maintenance cost rather than appearance.
The test that fell out of it: whether a system stays organized is decided by the cost of putting things back. The same test runs everywhere in engineering. If updating the documentation is painful, it rots. If registering a component is painful, people bypass the registry. If test setup is expensive, tests stop being written. A registry becomes a junk drawer when putting things away costs too much. When you find recurring debt, ask what the correct path costs before blaming discipline.
The limit, in both directions. Cleanup can become procrastination: the question is never "could this be cleaner?" but "is the current residue making the next meaningful step harder?" If yes, cleanup is part of the work; if no, continue. And the lowest-friction path is not always the safest one: security, auditability, and correctness can justify intentional friction. Remove the accidental friction and keep the controls.
Recognize it. Track it. Pay it down deliberately. Live with what you must. Technical debt is not inherently bad; unmanaged technical debt is. Manage yours.
Ward Cunningham first described the debt metaphor in his 1992 OOPSLA paper "The WyCash Portfolio Management System." He later clarified that he meant the debt of shipping code before fully understanding the domain, not the debt of writing bad code intentionally. ↩︎