Technical debt: which to pay, which to keep
Not all technical debt should be repaid. The useful question is not is this messy but does this cost us anything - and for a surprising amount of code the answer is no.
Every codebase has parts its developers are embarrassed by, and there is a standing temptation to fix them. Most of the time that is a poor use of the budget.
Debt is only debt if it charges interest. Ugly code in a file nobody has opened in two years is not costing anything.
The test
Three questions decide whether something is worth repaying:
- Does it slow down work we are actually doing? Not hypothetical future work - work on the roadmap now.
- Does it cause defects? Check the bug history rather than relying on a feeling.
- Does it block something we need, such as a platform requirement or a security update?
If all three are no, it is not debt. It is just code somebody does not like, and rewriting it spends real budget to buy an aesthetic improvement plus the risk of new bugs in a place that had none.
Untidy code that nobody touches and nothing breaks is not debt. It is paid off.
Let the history answer the first two questions
Both of the first two questions have answers already in the repository, and reading them takes less time than the argument they replace.
- Which files change most often. A file with a hundred commits is where the team actually works, and improvements there compound. A file with three is not on the path.
- Which files appear in fix commits. Cross-reference the churn list against anything whose message mentions a defect; the overlap is the genuinely expensive code.
- Where review comments cluster. If the same area generates long discussions every time, the cost is being paid in attention rather than in bugs.
The output is usually a surprise. The file everybody complains about is frequently stable and untouched, while the real cost sits in something nobody mentions because they have stopped noticing it.
The debt that genuinely compounds
Some categories reliably get worse and should be dealt with early:
- Anything in the data model. Every month adds records that must be migrated.
- Anything in authentication or permissions, where a mistake is a breach rather than a bug.
- Missing tests around the code that changes most often - the compounding is in the review time every change now needs.
- Dependencies far behind on major versions, because the upgrade path gets longer, not shorter.
The common thread is that time makes each of them worse without anybody touching the code. That is the real definition of compounding debt, and it is why they behave differently from the merely untidy: a messy view controller is exactly as expensive next year as it is today.
Repay it inside feature work
Refactoring projects are hard to sell, hard to schedule, and produce nothing a client can see. They also tend to be cancelled halfway, which is the worst outcome available.
Repaying debt inside feature work is easier to justify and safer: when you touch an area for a feature, improve it while you are there. The improvement is covered by the feature's testing and justified by the feature's value.
It has a limit worth naming. If the cleanup is larger than the feature, it stops being opportunistic and starts being a rewrite hidden inside a story, which is how a two-day feature becomes a fortnight and the estimate loses its credibility. At that point it deserves its own conversation and its own number.
The debt worth taking deliberately
Some of the most useful debt is incurred on purpose, and it is worth distinguishing from the accidental sort.
A hard-coded value in a feature nobody has validated yet is a reasonable trade. So is skipping an abstraction until there is a second case for it. The thing that makes deliberate debt safe is that somebody wrote down what it is and what would trigger repaying it.
Accidental debt is the same code with nobody aware of it. The code is identical; the difference is entirely in whether the team can see it.
Write it down
Keep a short list of known debt with a note on what it costs. Not a backlog of tickets nobody will action - a document, reviewed occasionally, that prevents the same decision being re-litigated every quarter and makes the trade-off visible when someone asks why a feature is taking longer than it should.
The most valuable entries are the ones that say we looked at this and decided not to fix it, with the date and the reason. Without them the same discussion happens every few months, costs an hour of senior time, and reaches the same conclusion.


