Technical debt is not bad code. Bad code is bad code — it should not have been written that way. Technical debt is a structural decision that was appropriate at one point and becomes inappropriate at another, as requirements change. The word “debt” is precise: you borrow speed or simplicity now, and you pay interest over time in the form of slower development, harder maintenance, and higher risk of failure.
The debt quadrant classifies technical debt by intent and awareness:
Deliberate/Reckless — “We don’t have time to do this right.” The team knows better, ignores it, and accrues liability. This is the only category that is genuinely bad practice.
Deliberate/Prudent — “We need to ship now and we’ll fix this later.” The team makes a conscious tradeoff, accepts the debt, and intends to repay it. The MVP shared database example is this category. Appropriate when the cost of the future fix is known, the benefit of speed is real, and the team will remember to repay.
Inadvertent/Reckless — “What’s layering?” The team lacks the knowledge to make better decisions. They produce debt without knowing it. Most dangerous: no intention to repay because the debt is invisible.
Inadvertent/Prudent — “Now we know we should have done it differently.” The team learns something through the work that they could not have known beforehand. The design that was correct for a simpler problem is wrong for the evolved problem. This is the natural consequence of discovery; it is not avoidable.
Measuring debt: abstract measures (“code quality”) are not actionable. Concrete measures are:
Lines changed per feature — if adding a payment method requires touching forty files, the abstraction is missing and debt is high. Track this over time. If the number increases, debt is accumulating.
Test coverage gaps — uncovered code is unverifiable code. Every change to uncovered code is an unknown risk. Coverage gaps identify where debt makes change risky.
Deployment frequency — how often does the team deploy? Low frequency is a symptom, not a cause. Teams deploy infrequently when deployment is risky, and deployment is risky when code changes have unpredictable side effects — which is what high-debt codebases produce.
Mean Time to Recovery (MTTR) — how long does it take to recover from an incident? High MTTR correlates with poor observability and complex, fragile systems — both symptoms of accumulated technical debt.
Technical debt compounds because it makes every subsequent decision more expensive. A service with high coupling between modules costs more to add features to — not linearly more, but multiplicatively more. Each new feature must navigate the existing coupling. Over time, the team spends an increasing proportion of their capacity on navigating debt rather than delivering value.
The interest rate can be quantified. A codebase where a medium-sized feature takes 3 weeks instead of 1 week — because of coupling, missing tests, and architectural inconsistency — is running at 200% interest. Every £1 of original debt costs £2 in delayed delivery.
Communicating this to non-technical stakeholders requires translating the interest rate into delivery terms: ‘We currently spend approximately 40% of engineering capacity on debt navigation. Addressing the three highest-interest components would recover 20 engineering-weeks per quarter.’ This is not a technical argument. It is a business case.
Communicating debt to stakeholders requires translating from technical symptoms to business risk. “We have high coupling in the billing module” is not a business problem. “Adding the next payment method will take eight weeks instead of two, and there is a 30% chance it breaks an existing payment method” is a business problem. Debt communication must be specific about the cost (time, risk) and the consequence of not addressing it (slower product velocity, higher incident rate).