Technical Debt: How to Measure It and Pay It Down
Key takeaway
Measure technical debt by its symptoms — lead time for changes, change failure rate, defect escape rate, and onboarding time — not by abstract code scores. Then fund a standing 15–20% of engineering capacity for repayment, tied to the areas you're about to build in.
Every engineering team says it has technical debt, and every executive has learned to discount the claim, because it's usually presented as a feeling rather than a number. The way out is to stop arguing about code quality and start measuring delivery.
Metrics that make debt visible
- Lead time for changes — how long from a merged commit to running in production. Debt shows up here first.
- Change failure rate — the share of deploys causing an incident or rollback.
- Defect escape rate — bugs found by customers versus by your own tests.
- Time to onboard a new engineer to their first meaningful commit.
- Percentage of engineering time spent on unplanned work — over 30% usually means the system is setting your priorities.
Not all debt is worth repaying
Debt in code that rarely changes costs you nothing. Debt in the module you touch every sprint costs you every sprint. Overlay your commit history on the codebase: the intersection of "changes often" and "is hard to change" is your entire repayment backlog, in priority order.
A funding model that survives roadmap pressure
- Reserve a fixed share of each sprint — 15–20% is typical — rather than requesting a separate cleanup project. Cleanup projects get cancelled; a standing allocation doesn't.
- Attach repayment to upcoming feature work: refactor the module you're about to extend, while the context is loaded.
- Express each item in delivery terms — "this removes two days of manual regression per release" — not in engineering aesthetics.
- Track one of the metrics above before and after, so the next request is backed by evidence.
When repayment isn't the answer
Sometimes the honest assessment is that a component should be replaced rather than improved — when it's on an unsupported runtime, when nobody understands it, or when the rate of incident is no longer bounded. Strangler-fig replacement, routing new functionality to a new implementation while the old one shrinks, is almost always safer than a big-bang rewrite.