Insights14 min read

What Is Technical Debt? A Guide for Budget Holders

By Niraj Jha ·

Co-Founder & CTO · Last updated

Key takeaways

  • Debt is only expensive where the code changes. The same mess in a module nobody touches costs nothing, and refactoring it is spending money on aesthetics.
  • The clearest symptom a budget holder can see is estimates becoming unpredictable - small-sounding changes repeatedly turning into a week.
  • Overlay change frequency against difficulty. The intersection is the entire remediation list, and everything outside it can usually wait indefinitely.
  • Fund it as a standing 15-20% allocation or as an explicit line in feature estimates. Big-bang refactoring quarters are unshippable and get cut when priorities move.
  • Rewrites are almost always more expensive than incremental replacement, because they discard years of undocumented fixes and rediscover them in production.

Your developers keep saying "technical debt" and you keep hearing "we would like to spend your money tidying up". That translation is understandable and mostly wrong, and the misunderstanding costs businesses more than the debt itself.

The metaphor is precise, which is why it has survived. Ward Cunningham coined it and Martin Fowler has written the clearest explanation: you take a shortcut to ship sooner, and you pay interest on that shortcut every time you touch the code afterwards. Like financial debt, some of it is sensible leverage and some of it is ruinous. The skill is telling them apart.

This is a guide for the person approving the budget rather than the person writing the code.

The metaphor, taken seriously

Borrowing to buy a business asset is rational. Borrowing at 30% to cover payroll is not. The debt is not the problem; the interest rate and what you bought with it are.

Technical debt works the same way:

Deliberate and prudent. "We will hard-code the tax rate to make the trade-show deadline, and fix it in January." Written down, scheduled, and the shortcut bought something real.

Deliberate and reckless. "We do not have time for tests." Bought speed today at an unbounded cost later.

Accidental and prudent. "Now that we have built it, we understand the problem better and would model it differently." This is not really debt at all; it is learning, and it is unavoidable.

Accidental and reckless. "What is a data model?" This is not debt, it is damage.

The important distinction for you: the first kind is a decision, and the others are not. A team that names its shortcuts, writes them down and schedules them is managing debt. A team that never mentions it is accumulating it invisibly.

How it shows up on your side of the wall

You will never see the code. You will see these.

Estimates stop being predictable. The clearest symptom. A change that sounds small keeps turning into a week, and nobody can tell you in advance which ones will.

The same bug comes back. Fixed in March, back in July. Usually means the fix was local rather than structural, and there is no test preventing the regression.

Fixing one thing breaks another. Components are entangled, so a change here has consequences there that nobody predicted.

Onboarding a developer takes a month. The system cannot be understood from the code; it has to be explained.

Nobody wants to touch a particular area. There is one module everyone routes around. That is a debt concentration with a name.

Releases get less frequent and more frightening. Deploying is a ritual with a runbook and a nervous atmosphere, so it happens monthly instead of daily - which the DORA research consistently identifies as the signature of a struggling delivery system.

A useful executive question: "If I asked for this change, how confident are you in the estimate, on a scale of one to five?" Consistent threes and twos on small changes is technical debt speaking, and it is a more honest signal than asking about the code directly.

What it actually costs

The interest is real and it compounds. A rough shape of what accumulating debt does to delivery capacity:

Debt levelSymptomShare of effort lost
LowOccasional surprise, estimates mostly hold5 - 10%
ModerateRegular rework, some areas avoided15 - 25%
HighMost changes take 2-3x the naive estimate30 - 50%
SevereFeature work has effectively stopped50%+

Those are illustrative rather than measured, but the shape is what matters: it is not linear. Debt gets more expensive faster than it accumulates, because each shortcut interacts with the others.

The commercial translation: at "high", you are paying two engineers to get the output of one. If your team costs $200,000 a year, that is $80,000 annually in interest, invisible on every invoice.

Not all of it is worth paying down

This is the part engineers sometimes get wrong and the part that makes budget holders sceptical of the whole conversation.

Debt in code that never changes costs nothing. An ugly module written five years ago, touched twice since, working correctly, is not costing you anything. Interest is only paid when you touch it. Refactoring it is spending money for aesthetics.

Debt in code you are about to delete is free. If that feature is being replaced next quarter, leave it alone.

Debt in code that changes weekly is where all the cost is. Same amount of mess, completely different interest rate.

Ask your team to overlay two things: which files change most often, and which are hardest to work in. The intersection is your entire remediation list. Everything outside it can usually wait indefinitely, and saying so out loud makes the remediation ask much easier to approve.

How to fund it without a "cleanup project"

Big-bang refactoring projects are hard to approve, hard to justify midway, and frequently abandoned. These approaches work better.

The percentage rule. A standing allocation - commonly 15 - 20% of each cycle - for debt work, chosen by the team. No approval theatre per item. Simple, and it stops debt accumulating faster than it is paid.

Pay as you go. Whenever you touch an area for a feature, leave it slightly better. Cheap because you are already there with the context loaded.

Debt as an explicit line in the estimate. "This feature is five days, or eight days including fixing the module it sits in, which will make the next three features faster." Now it is a business decision with visible upside rather than a request for tidying.

Fix it when it blocks something. Perfectly legitimate. The module nobody wants to touch gets fixed the quarter you need to change it.

Do not: schedule a quarter of pure refactoring. It is unshippable, unverifiable from the outside, and the first thing cut when priorities move.

A worked example

A five-year-old platform, four engineers, feature velocity roughly halved over eighteen months.

The team mapped change frequency against difficulty and found three areas accounting for most of the pain:

AreaChanges/quarterProblemFixEffort
Pricing engine~20No tests, 900-line functionExtract and test12 days
Order status~15Logic duplicated in 5 placesConsolidate6 days
Reporting queries~8Slow, no indexesIndex and cache3 days
Legacy import tool~0Genuinely awfulLeave it alone0 days

21 days of work. The fourth row is the important one: the worst code in the system was correctly left untouched, because nobody had modified it in a year.

Six months later, estimates on pricing changes were holding, and the team's own assessment was that a typical pricing feature had gone from around eight days to around four. That is a real return on 21 days, and it was approvable because it was specific, time-boxed and tied to a business outcome.

Preventing it in the first place

Most debt is not a coding problem, it is a process problem.

Automated tests on the paths that matter. Not 100% coverage. The critical flows, so a change that breaks them fails immediately rather than in production.

Code review by someone who will maintain it. A second pair of eyes catches the shortcut before it is permanent.

Deployment that is boring. If shipping is easy, changes are small. Small changes create less debt than big ones.

Dependency currency. Being current is cheap; catching up is a project. See what maintenance actually costs.

A written record of deliberate shortcuts. A file in the repository listing what was hard-coded, what was skipped, and why. Debt you can see is debt you can manage.

Realistic deadlines. The single biggest cause. Sustained pressure to ship faster than is possible produces debt reliably, and the debt then makes everything slower, which produces more pressure.

Debt you took deliberately versus debt that happened to you

The distinction matters more than the metaphor usually allows. Deliberate debt - a shortcut taken knowingly, recorded, with a rough idea of what repaying it costs - is a legitimate financing decision and frequently the right one under a deadline. Accidental debt is the code nobody chose, produced by a team that did not know a better shape existed or had no time to find one.

They need different responses. Deliberate debt needs a date and an owner, and it gets repaid. Accidental debt needs someone to notice it, which is what code review and a periodic look at where changes are slow are actually for. A team that records the first and never looks for the second will still end up where this article started, just more tidily.

Objections, answered

"How do I know they are not just gold-plating?" Ask for the change-frequency overlay. A team asking to fix code that changes twenty times a quarter has a case. A team asking to rewrite something nobody touches is doing aesthetics. The question is entirely reasonable to ask and a good team will welcome it.

"Can we just do a rewrite instead?" Almost always the more expensive option. A rewrite discards years of accumulated small fixes, most undocumented, and rediscovers those problems in production. Incremental replacement using the strangler pattern is usually cheaper and always less risky. Rewrites are justified when the platform is unsupported or the architecture genuinely cannot support the direction, not when the code is untidy.

"Our developers created this debt, why should we pay to fix it?" Some of it, fairly. Much of it was created by decisions about deadlines and scope that were not theirs. And the accidental-prudent category - "we understand the problem better now" - is unavoidable on any project that learns anything.

"We are replacing this system in two years anyway." Then pay down only what blocks the next two years of work, and let the rest go. That is a legitimate and well-reasoned strategy. Say it out loud so the team can plan against it rather than quietly fighting the debt on your behalf.

"Can we measure it?" Partially. Useful proxies: estimate accuracy over time, change failure rate, time from commit to production, and how long a new developer takes to make a first meaningful change. None is perfect; the trend in all four is informative.

"Is some debt healthy?" Yes. A team with zero technical debt has probably over-engineered and shipped too slowly. The goal is to borrow deliberately, at a rate you can service, for something worth having.

Talking about it with your team

The conversation goes badly in predictable ways. These reframings help.

Instead of "how much technical debt do we have?" ask "which parts of the system slow us down most?" The first invites an unbounded list; the second produces a prioritised one.

Instead of "can you clean this up?" ask "what would make the next three features faster?" Ties the work to outcomes you can verify.

Instead of "why did this happen?" ask "what would have to change for it not to happen again?" The first is an autopsy and gets defensive answers; the second is a process question.

Instead of "can we afford this?" ask "what does not doing it cost per quarter?" Puts both options on the same footing.

And when the team says a change will take longer than you expected, the most useful follow-up is "what would it take to make this kind of change fast?" Sometimes the answer is a week of work that pays back within two months.

A quarterly review that takes an hour

Debt is manageable when it is looked at regularly and unmanageable when it is looked at in a crisis. Once a quarter:

1. Estimate accuracy. Of the last ten estimates, how many were within 25%? A declining trend is the earliest reliable signal.

2. The avoided-area list. Ask which parts of the system people dislike touching. Compare with last quarter. New entries matter more than old ones.

3. Change frequency overlay. Which files changed most? Intersect with the avoided list. That intersection is the remediation backlog.

4. Dependency and platform currency. How far behind, and what is the next end-of-life date.

5. Pick one thing. Not five. One item, time-boxed, with a stated expected benefit. Do it next quarter and check whether the benefit appeared.

That last discipline - checking whether the predicted improvement actually happened - is what turns debt work from an article of faith into something you can fund confidently.

How we handle it

Deliberate shortcuts are written down at the moment they are taken, with what was skipped and what it would cost to fix. That list is visible to you, so debt is a decision you are part of rather than something discovered later.

On existing codebases we start with the change-frequency overlay rather than an opinion about code quality, because where the cost is and where the mess is are not the same place.

If your estimates have stopped being predictable and you want a read on why, tell us about the system.

Where debt actually comes from

Understanding the sources helps you prevent the next round rather than only paying for the last one.

Deadline pressure, sustained. The largest single source by a distance. Occasional pressure is survivable and sometimes necessary. Continuous pressure produces debt reliably, and the debt then makes everything slower, which produces more pressure. This loop is the main reason teams get slower over time, and it is a management decision rather than an engineering one.

Requirements that changed after the design. A data model built for one set of assumptions, then bent to fit another. Rarely anyone's fault and largely unavoidable, but it is why revisiting the model after the first real usage is money well spent.

Turnover. Every departure takes context. The next person makes a locally reasonable decision that a person with the full picture would not have made.

Success. A system built for 500 users now has 50,000. Nothing was done wrong; the assumptions simply expired. This is the most pleasant source of debt and it still costs money.

Copy-paste under time pressure. Duplicating a function is faster today and means the next fix has to be applied in five places, four of which someone will miss.

Dependencies left to age. Cheap to stay current, expensive to catch up. Covered in what maintenance costs.

Nobody owning the whole picture. Several people making individually sensible decisions with no shared model produces a system that is coherent nowhere.

Notice how few of these are about developer skill. Debt is mostly a by-product of how work is organised, funded and deadlined - which is why it is a leadership topic rather than an engineering one.

A short script for the funding conversation

If you are an engineer trying to get debt work approved, this framing works better than any technical explanation:

"Changes to the pricing module currently take about eight days. Most of that is working around a function that has no tests and does five things. Twelve days of work would bring a typical change down to roughly four days. We make around six pricing changes a quarter, so it pays back within two quarters and keeps paying after that."

Four elements: the current cost, the specific cause, the expected improvement, and the payback period. No mention of code quality, elegance or best practice - none of which are things a budget holder can evaluate.

And then, crucially: come back a quarter later and report whether the predicted improvement actually appeared. Doing that once makes the next request far easier to approve, and being honest when it did not appear makes it easier still.

Debt in a system you are about to buy

If you are acquiring a business, taking over an application, or inheriting a codebase from a previous supplier, technical debt is a due-diligence item with real financial consequences. A day of investigation tells you most of what you need.

What runtime and framework versions, and are they supported? The first question, because anything past end of life is a mandatory project rather than a choice.

How long does it take to get running locally? If a competent developer cannot clone the repository and have it running within an hour, the handover is incomplete and the documentation deficit is itself a cost.

Can it be deployed right now? Ask for a deploy of an unchanged version. Surprisingly often this fails, and a system you cannot deploy cannot be patched, which makes every future problem an emergency.

What does the test suite cover? Not the percentage. Ask whether the critical business flows are covered, and watch someone run the suite.

How many dependencies, and how many advisories? A single command answers this, and the number of unresolved critical advisories tells you how the previous team operated.

What is the change history? A repository with a steady commit history and reviewed pull requests is a different asset from one with sporadic bulk commits from a single account.

Who understands it, and are they staying? The most important question and the least technical. Documentation is a poor substitute for the person who made the decisions.

Price the answers into the deal. A system needing a runtime upgrade, deployment automation and a test baseline before anyone can safely change it is carrying maybe 20-40 days of work before it delivers any new value, and that is a number you can negotiate with.

Related reading

Sources and further reading

Article FAQ

Questions,
answered

More on What Is Technical Debt? - the follow-ups we get asked most, answered the way we would answer them on a call.

A shortcut taken to ship sooner, which you then pay interest on every time you touch that code. Some of it is sensible leverage and some is ruinous - the difference is the interest rate and what the shortcut bought.

Have a product to build?

Shunya ships production software - web applications end to end - with one team that owns the whole stack from concept to launch. Tell us what you want to build.

Niraj Jha

Written by

Niraj Jha

Co-Founder & CTO

Co-Founder & CTO of Shunya Tech. Full-stack architect who sets the engineering culture and technical standards behind every product we ship - from database design to production delivery on Next.js, tRPC, and Prisma.

Last updated