Every codebase past its first few months has technical debt. The question a founder or CTO actually needs answered is not “how much debt do we have” but “which parts of it are costing us money right now, and which are just untidy.” Those are different problems, and treating them the same way is how teams end up either rewriting things that were fine or shipping features on top of a foundation that is quietly getting more expensive to touch.
This matters most at the point of a decision: commissioning a new feature, choosing between a rebuild and an extension, or working out whether a development partner’s estimate for “just adding X” is realistic. Technical debt is one of the few engineering concepts that a non-technical decision-maker genuinely needs a working model of, because it changes what a reasonable quote looks like.
Debt is not one thing
The term gets used for three different situations, and they call for different responses.
Deliberate debt is a shortcut taken on purpose, usually to hit a launch date or validate an idea before investing further. A payments flow that only supports one currency because the business only sells in one market is deliberate debt. It is not a mistake — it is a bet that the shortcut will either be removed later or never need removing at all.
Accidental debt is what accumulates when requirements shift faster than the code does. A user model built for individual accounts slowly grows team features bolted on the side, because nobody stopped to redesign it when the product direction changed. Nobody chose this; it is the residue of the business evolving.
Aged debt is code that was reasonable when written and has become a liability through no fault of the original decision — a library that stopped receiving security updates, a cloud service that got deprecated, a pattern that used to be idiomatic and is now the thing every new hire has to be told not to copy.
Knowing which category you are looking at changes the conversation. Deliberate debt needs a decision about whether the bet paid off. Accidental debt needs a decision about whether the product direction has stabilised enough to justify a redesign. Aged debt needs a decision about timing and risk, because the clock is not fully in your control.
The metric that matters is contact frequency, not age or ugliness
A common mistake is prioritising technical debt by how bad the code looks. Ugly code that nobody touches is not costing the business anything. The debt worth addressing is the code that sits on the path of features you are actually building next.
A practical way to sort it: for anything flagged as debt, ask how often it will be touched over the next two or three commissioned pieces of work. Code that every new feature has to route through or extend is high-contact. Code that was written once, works, and is unlikely to be touched again is low-contact, regardless of how it was built.
High-contact, low-quality code is where the real cost lives — every change through it takes longer, carries more risk of breaking something adjacent, and makes estimates from a development partner less reliable, because nobody can be fully sure what a change will disturb. Low-contact, low-quality code can usually be left alone. Fixing it is tidying, not investment.
Three responses, and how to choose between them
Once a piece of debt is identified as high-contact, there are three honest responses, not two.
- Fix it properly. Justified when the code is on the critical path for multiple upcoming features and the fix is scoped and boundable — you can describe what “done” looks like before you start.
- Work around it. Often the right call when the debt is real but a full fix would require touching more of the system than the upcoming feature warrants. A workaround should be named as one, with a note on what it constrains, rather than absorbed silently into the feature’s scope.
- Leave it alone. The correct answer more often than teams admit. If nothing on the roadmap touches it and it is not causing incidents, spending budget on it is optimising for a problem you do not currently have.
What should not happen is treating every piece of debt as automatically “fix it” or automatically “later.” Both defaults remove the actual decision, which is a cost-versus-contact judgement specific to what you are building next.
What this looks like when you commission the work
If you are not doing the engineering yourself, the practical skill is asking the right question of whoever is. A development partner that has looked honestly at your codebase should be able to tell you, for a given feature request, three things.
- Which parts of the existing system the feature will touch, and whether any of those parts are known to be fragile.
- Whether the estimate assumes working around the fragile parts or fixing them first, and what that assumption does to the timeline.
- What happens to the estimate if the fragile part turns out worse than expected once they are inside it.
A vendor who cannot answer these, or whose estimate does not change whether the underlying code is solid or shaky, has probably not actually looked. That is a bigger risk than the debt itself, because it means the next few estimates will not be reliable either.
A decision checklist
Before committing budget to “fixing technical debt” as a line item, it is worth running through the following.
- Is this deliberate, accidental, or aged debt — and does that change what “fixing” it even means?
- How many upcoming pieces of work will actually touch this code? If the honest answer is “none we can name,” it does not belong in the next quarter’s plan.
- Can the fix be scoped with a clear definition of done, or is it really “start refactoring and see how far we get”? The second is a research task, not a commissioned deliverable, and should be budgeted differently.
- If we work around it instead, what does that workaround constrain for the next twelve months, and who is tracking that constraint?
- Who owns the decision to revisit this later, and what would make them revisit it?
None of this requires a rewrite to answer. It requires treating technical debt as a set of specific, named trade-offs rather than a single number to feel bad about.