Engineering · April 2026 · 8 min read
What Is Technical Debt, and When Should You Pay It Down?
Why features slow down over time, which debt is worth carrying, and how to talk about it without jargon.

Technical debt is the gap between how your software is built and how it would need to be built to change easily. Like financial debt, it is not automatically bad - it is borrowed speed, and it charges interest.
The interest shows up as time. A change that would have taken a day takes three, because it has to be made in four places, and nobody is certain what else depends on it.
What it looks like from outside engineering
Founders rarely see technical debt directly. They see its symptoms, and usually attribute them to the team rather than the codebase.
- Estimates keep growing for features that sound similar to ones you already have.
- Every release breaks something unrelated to what changed.
- Only one person can safely touch a particular area.
- Small changes require a disproportionate amount of testing.
- Engineers start sentences with "we should really rewrite...".
If the same feature would have taken two weeks a year ago and takes six now, that difference is the interest payment.
Not all debt is the same
Lumping everything together is what makes these conversations unproductive. There are roughly four kinds, and they deserve different responses.
| Kind | How it happened | What to do |
|---|---|---|
| Deliberate and documented | A conscious shortcut to hit a date, written down | Fine. Revisit when the reason expires |
| Deliberate and forgotten | A shortcut nobody recorded | Cheapest to fix once found - mostly a documentation problem |
| Accidental | The team learned something later that invalidated an early design | Normal and unavoidable. Fix when you next touch that area |
| Structural | The foundation cannot support what the product became | The expensive one. Needs a deliberate decision, not a backlog ticket |
Only the last category justifies stopping feature work. The first three are handled by doing ordinary engineering carefully.
Where it comes from
Debt accumulates for reasons that were sensible at the time.
Deadlines are the obvious one: the shortcut that gets a release out is genuinely the right call when the alternative is missing a launch. Changing requirements are another - a data model designed for one product shape is carrying debt the day the product becomes something else, without anyone having made a mistake.
Turnover contributes more than people expect. Code written by someone who has left is harder to change not because it is bad, but because the reasoning behind it went with them.
And increasingly, generated code contributes: it is fast to produce and easy to accept without anyone forming a view on whether it fits the structure around it.
How to decide what to fix
The useful question is not "is this debt?" - most of a codebase is, by some standard. It is "is this debt costing us anything?"
Debt in code nobody touches costs nothing. An inelegant module that has not needed changing in two years can stay inelegant indefinitely. Debt in the area you ship to every week is expensive, and paying it down returns time immediately.
So the practical test has two parts: how often do we change this, and how much does each change hurt? Anything that scores high on both is worth fixing now. Anything low on either can wait, possibly forever.
Fix the debt that sits where you work, not the debt that offends you most.
What to do about it
Three approaches, in order of how often they are the right answer.
- Fix it as you pass. When a feature takes you into a messy area, leave it slightly better. This handles most debt at almost no visible cost.
- Schedule a specific fix. For a known problem with a known cost - a fragile payment path, a slow query - book it as work with a defined outcome, not "refactoring time".
- Rebuild a subsystem. Only when a part of the system genuinely cannot carry what you need, and with a comparison of what maintaining it would cost instead.
Be wary of the full rewrite. It is almost always more expensive and slower than expected, and it reproduces the original's subtle behaviours only after painful rediscovery. Partial, deliberate replacement of a specific subsystem is nearly always the better trade.
Talking about it with your team
Engineers often raise debt in technical terms, which makes it sound like preference. The translation that makes it a business conversation is cost and risk.
Ask: what does this cost us per feature right now, what does it prevent us from doing, and what happens if we leave it for another year? An engineer who can answer those has identified real debt. One who cannot may be describing a style disagreement - which is fine, but it is a different conversation.
If you want an outside read on where a codebase actually stands, that is what our code audit.
Frequently asked questions
Is technical debt always bad?
No. Taking a deliberate shortcut to hit a launch date is often the correct decision, in the same way borrowing money can be. It becomes a problem when it is invisible, undocumented, and sitting in the part of the system you change most often. The goal is to carry debt knowingly rather than to have none.
How do I know how much technical debt my app has?
The most reliable signal is not a tool but a trend: compare how long comparable features took a year ago versus now. If similar work is taking meaningfully longer, that difference is the interest you are paying. A code audit can then tell you where it is concentrated.
Should we stop building features to fix technical debt?
Rarely. Most debt is best handled inside normal feature work, by improving the areas you are already touching. Stopping entirely is justified only when the foundation genuinely cannot support what comes next - and that case should come with an explicit comparison of what continuing to maintain it would cost.
Does AI-generated code create technical debt?
It can, quickly. Generated code is fast to produce and easy to accept without anyone deciding whether it fits the structure around it, which tends to produce duplicated logic and inconsistent patterns. Used with review it is fine; used as a substitute for review it accumulates debt faster than hand-written code, because nobody ever held the design in their head.
Is a full rewrite ever the right call?
Occasionally, but far less often than it feels like. Full rewrites tend to run long and lose subtle behaviours that were never documented but that users depend on. Replacing one subsystem at a time, while the rest keeps running, gets most of the benefit at a fraction of the risk.
More posts
View AllHave a project in mind?
Tell us what you are building and we will come back with a scope, a timeline and a price.


