Skip to content
Apsan Works

Technical Debt Triage: Deciding What to Fix and What to Leave Alone

Most technical debt is not worth paying down. The debt that matters is the kind that slows every change or threatens something the business depends on.

5 min read
Two-by-two chart of technical debt by monthly cost and cost to fix: fix now when cost is high and the fix is cheap, plan it when both are high, do it opportunistically when both are low, and leave it alone when cost is low and the fix is expensive.

Every codebase carries debt, and every engineering team has a list of things they would clean up if given the time. The list is usually long, and the temptation is either to ignore it entirely or to treat all of it as urgent. Both are mistakes.

The useful question is not how much debt exists. It is which pieces of it are costing something now, and which are merely untidy. Triage means sorting them honestly, because the answer is that most debt is fine to leave where it is.

Debt is only a problem when it charges interest

The metaphor is useful because it points at the right measure. A loan you never touch and that costs you nothing is not a problem. What matters is the interest: the ongoing cost the debt imposes.

Ugly code in a corner nobody changes costs nothing. A confusing module that every feature has to pass through costs a little each time. A fragile deployment process that breaks one release in five costs a lot, and the cost compounds because it makes everyone more cautious.

So the first step is to stop judging debt by how it looks and start judging it by how often it hurts. Code that is hard to read but stable and rarely touched can usually stay.

The signals worth acting on

A few patterns reliably indicate debt that is charging real interest.

Change is slow in a specific area. If simple features in one part of the system routinely take far longer than they should, because of tangled logic or missing tests, that area is taxing everything that touches it.

Bugs cluster. The same module producing repeated defects is telling you something about its structure.

People avoid it. When engineers route around a part of the system, or hesitate to change it, the fear is a cost in itself, and it usually has a good reason behind it.

It blocks something that matters. Debt that prevents a needed feature, a security fix, a framework upgrade, or a migration has a price that can be named, which is the strongest case for paying it.

It carries risk. Outdated dependencies with known vulnerabilities, missing backups, and a system only one person understands are not matters of taste. They are exposure.

Sort by cost and by cost to fix

A simple two-part judgement does most of the work. For each item, estimate how much it costs you per month in time lost, defects, or risk, and how much it would cost to fix.

High cost and cheap to fix is the obvious first move. High cost and expensive to fix needs a plan, and often a staged one. Low cost and cheap to fix can be done opportunistically when someone is already nearby. Low cost and expensive to fix is the category to leave alone, however much it irritates people.

The estimates do not need to be precise. They only need to be honest enough to separate the items that matter from the pile.

Fix debt where you are already working

The cheapest time to improve code is when you are already changing it. Tidying a module while adding a feature to it costs a fraction of a dedicated clean-up, and it targets exactly the places that are being touched, which are the places where the interest is actually being paid.

This is more reliable than scheduling large refactoring projects, which tend to get postponed, expand in scope, and deliver less than promised. Small, continuous improvement in active areas tends to beat occasional heroic rewrites.

Be suspicious of the big rewrite

The most seductive form of debt reduction is the full rewrite, and it is also the most dangerous. A rewrite discards years of accumulated fixes for edge cases that nobody remembers, and it delivers nothing to users until it is finished.

When a system genuinely cannot continue, the safer path is gradual replacement, as described in migrating off legacy software: run the new alongside the old, move one segment at a time, and keep a way back. The same logic applies at smaller scale. Replace pieces, not everything at once.

Make the debt visible and decide deliberately

Some debt is taken on intentionally, to ship sooner, and that can be the right call, especially for a first version, as in scoping an AI MVP. The failure is taking it on silently and forgetting it exists.

Keep a short, honest record: what the shortcut was, why it was taken, and what would trigger paying it back. That turns debt from a vague unease into a decision with an owner. A short list that is reviewed regularly beats a long backlog nobody reads.

Protect the time to do it

Debt work loses to feature work unless it is protected. Reserving a modest, steady share of capacity for maintenance is usually more effective than waiting for a quiet period that never arrives. Framing it in terms of cost helps: a fix justified by faster delivery or lower risk is easier to defend than one justified by tidiness.

When to leave it alone

If a system is stable, rarely changed, and about to be retired, paying down its debt is waste. If the business is about to change direction, the debt in the old direction may never matter. Spend the effort where change is frequent and the cost is real, and let the rest be.

We help teams keep systems healthy without stopping delivery. See how we work on applications, or read about choosing a tech stack for an MVP.

Tell us what is slowing you down

A short conversation is usually enough to tell whether this is a build, an automation, or something you should not do at all. We will tell you which.