Where the word came from, and why the origin matters
Ward Cunningham coined the metaphor to explain to finance people why his team was spending time rewriting code that already worked. His point was not that bad code is debt. His point was that shipping an imperfect first version lets you learn faster, and that what you learn then has to be put back into the code. If you never put it back, you pay interest on everything you build on top.
It is a financial metaphor and it was meant as one. It describes borrowing on purpose. It does not describe an accident.
Today the phrase covers roughly anything anyone dislikes about a codebase. That is a loss, because the original version of the idea was useful and precise.
I should say up front that we sell refactoring and legacy work. So when I argue below that a good share of what gets called technical debt should be left alone, read it knowing that the argument runs against our own short-term commercial interest.
Deliberate debt, and ordinary mess
Two very different things share the name.
The first one. A team knows how it should be built, decides not to build it that way, and writes down why. You hardcode three special cases instead of writing the rules engine, because the release is in six weeks and a general engine adds nothing to that release. That is a trade. It bought something, it has a maturity date, and the repayment cost can be estimated. It is a financing decision, no different in kind from a working capital loan, and it can be an excellent one.
The second one. Nobody decided anything. Business logic spread across four layers because three developers passed through without ever speaking to each other. That is not debt. That is mess. No money was borrowed, so nothing was bought with it.
Collapsing the two is why the conversation with finance goes badly. You walk in saying "we have technical debt", which sounds like a decision you own. The person across the table hears "we borrowed to fund something", and quite reasonably asks what the borrowing paid for. If the honest answer is "nothing, it just happened", you have lost the conversation, and you will lose it again next quarter.
So separate them when you speak. Here is what we chose to borrow in order to ship in November. Here is what degraded because nobody was watching. Both cost money. Only one of them bought anything.
Why it compounds
Normal debt has a schedule. You know when you pay and how much. Technical debt has no schedule. It takes a cut of every subsequent change, and the cut grows.
The mechanism is not subtle. The shortcut taken in January becomes something two features lean on by March. By June there are six, and routing around it properly means touching all six. The repayment cost has tripled and nobody did anything wrong in between. You did not get the six features cheaply either. Each one cost slightly more than the last, because each had to work around the shortcut.
That is the compounding half of the metaphor, and it is real.
The awkward part is the form the payment takes. No interest line appears in any account. It is paid in slowness: estimates that inflate for no visible reason, developers who get cautious, small changes that go through review three times, one more manual check before every release because the last one broke something unrelated.
None of that has a ledger entry. Which is exactly why it stays invisible until it is expensive. By the time it surfaces at board level, what gets said is not "we have a debt problem", it is "engineering has slowed down", which is the same fact reported by someone who does not have the vocabulary for it.
Making it legible to someone who does not read code
Most engineers argue this case with adjectives. The code is messy, brittle, unreadable, badly structured. A finance director cannot act on an adjective. They have no way to know whether "messy" costs ten thousand or three hundred thousand, and because they cannot know, they defer.
What works is talking in delivery time and risk.
Delivery time first. Pick a category of change you make regularly: adding a field to a form, wiring up a new payment method, shipping a report. Look at how long it took eighteen months ago and how long it takes now. Your Git history and your board give you both numbers without inventing anything. If there is a gap, you no longer need adjectives. The same request now costs more than it used to, and here is how much more.
Then risk. How many people in the company can change this module unsupervised. If the answer is one, you do not have a code problem, you have a continuity problem, and every executive understands that instantly. Add the share of production incidents in the last six months that trace back to the same area. The concentration makes the argument for you.
Then put a number on the real lifetime of the software rather than on the current financial year, because that is where debt becomes visible. We worked through that arithmetic in our piece on the five-year total cost of custom software. The shortcut that saved three weeks in year one gets repaid in year three, usually with a multiple attached.
And bring an option with a price on it, not a warning. "Six weeks on this module, after which changes of this kind go back to what they used to cost" is an investment proposal. "We need to refactor" is not.
Rebuild or repair
This is the most expensive decision in the whole subject, and the one engineering teams get wrong most often, us included. We have argued for rewrites we should not have argued for.
A full rewrite is nearly always longer, costlier and riskier than it looks. Three reasons.
The old system holds years of edge cases nobody documented. They are not in the specification, they are in the code, and you discover them in production one at a time as customer complaints. Second, during the rewrite you have to keep both systems alive, which means fixing things twice and constantly choosing between feeding the old one and advancing the new one. Third, you estimate the rewrite against the clean idea you hold of the system, not against its actual behaviour, and actual behaviour is the only scope that counts as finished. That is the most reliable estimation error in this industry.
Add that the product roadmap is frozen throughout. That freeze is a commercial cost and it appears nowhere in the project budget.
Repair wins when the system does the right job, its platform is still supported, and there is enough test coverage, or enough people who know it, to change it without breaking it. In that case you isolate the painful area, put a stable interface around it, and replace it from the inside. It is slow and unglamorous, but it ships in small pieces, each one verifiable, and you can stop at any point without losing what is already done.
Rewrite wins in cases you can name. When the platform is out of support and no longer receives security patches, somebody other than you has already made the decision. When nobody understands the system, there are no tests, and the documentation is wrong, there is nothing to stand on while repairing. When the business has genuinely changed, so parity is not the goal and half of what the old system does should not be rebuilt at all. And when the system is small enough to redo in less time than your normal decision cycle, in which case the risk is low because the window is short.
One practical test. If you cannot describe, on a single page, what the old system does, you are not ready to rewrite it. You are ready to rewrite your idea of it, which is a different project.
Not all debt is worth repaying
This is the part people in my trade say too rarely.
Debt in a module nobody touches costs nothing. The billing code written six years ago, opened by two people since, running without incident, nowhere near the roadmap, can stay exactly as it is. It is ugly. It works. Cleaning it up would burn weeks for no return, because the interest is only charged at the moment you make a change.
Good prioritisation crosses two pieces of information you already have. Where the pain is real, and where the work is actually going to happen over the next twelve months. Git history gives you the first half almost for free: the files that concentrate commits and bug fixes are your hot spots. The product roadmap gives you the second. The intersection is the only debt worth repaying this year.
Anything painful but stable, leave it. Anything clean and busy, good. And if a hot spot sits in an area you plan to retire next year, leave that too.
This is an investment call, not an aesthetic judgement, and it is exactly the kind of decision our clients bring in a CTO as a service for rather than hiring. You need someone able to say no to a rewrite the team is asking for, and to defend that answer to the board.
What refactoring will not fix
Some of what gets labelled technical debt is not debt at all.
Sometimes the problem is a product one. The feature is heavy to maintain because it tries to serve four different use cases that should never have shared one implementation, and it tries to serve them because nobody ever decided which customer the product is for. The code is not complicated by accident. It is complicated faithfully. Clean it up without making that decision and you get a tidy implementation of the wrong idea, and you will be back in eighteen months.
Sometimes the problem is organisational. Four teams change the same module with no clear owner. Every release needs three approvals that take a week each. Nobody dares delete code because nobody knows who depends on it. None of that is fixable in a text editor. Those are ownership, mandate and decision problems, and a refactor will only make them more visible.
The rule I use is short. Refactoring fixes neither a product that answers the wrong question nor an organisation that cannot decide. It only makes execution cheaper once the question and the decision are settled.
What we actually do about it
When a client calls us about this, we rarely start with code. We read the history, we measure where changes are slowing down, and we ask where the roadmap is going. What comes out is a short costed list, including what we recommend leaving alone, which is usually half of the original list.
After that, either your team does the work and we supervise it, or we take the module and hand it back in a state your developers can live in. That second shape falls under our nearshore development team offer, and it is only worth doing if you take the wheel again afterwards.
One last honest point. If your team has been saying the system is slowing down for two years and nothing has moved, the problem is probably not technical. It is that nobody ever translated the warning into a costed investment decision with an option to accept or refuse. Start there. It costs nothing, and it is almost always the missing step.
