What is technical debt, and when do you pay it down? Measuring, prioritizing, budgeting
Technical debt is the interest you pay tomorrow on the design decisions that let you move fast today. It is not the same thing as sloppy code, though the two get conflated constantly. Messy code is just mess; debt is an obligation you took on, deliberately or not, and can pay back. Four things make it manageable: talk about it in units of time rather than as a feeling, rank it by change frequency instead of by scanner totals, pay it inside work you were going to do anyway rather than in a separate cleanup sprint, and write down what share of capacity goes to it. Get those four right and debt stops being a complaint. It becomes a line item you can plan.
What the 1992 metaphor actually said
Ward Cunningham coined the term while building the WyCash portfolio management system, and described it this way in his OOPSLA'92 experience report: shipping first-time code is like going into debt, and a small debt speeds development up so long as it gets paid back promptly with a rewrite. The danger is in never repaying it. Every minute spent on not-quite-right code counts as interest.
Cunningham revisited the metaphor in 2009 because the common reading had drifted. He was never arguing that you should write code badly and fix it later. What he meant was the growing gap between what you have learned about the domain by shipping and what your code currently expresses. Martin Fowler maps this onto two axes: was the debt deliberate or inadvertent, and was it prudent or reckless. That gives four quadrants, and they do not deserve equal attention. The one Fowler singles out is prudent-inadvertent: it can take a year of programming on a project before you understand what the best design would have been, so this kind of debt shows up even on excellent teams.
The numbers move this out of engineering and into management
In Stack Overflow's 2024 developer survey, 62.4% of professional developers named the amount of technical debt as their top frustration at work. The second and third items on that list, complexity of the tech stack for building and for deployment, came in at 32.9% and 32.3%. One item carried roughly twice the weight of anything else.
The view from the top of the org looks similar. McKinsey surveyed 50 CIOs at financial services and technology companies with revenue above $1 billion and reported that 10 to 20 percent of the technology budget earmarked for new products gets diverted into resolving tech debt issues. Those same executives put total tech debt at 20 to 40 percent of the value of their entire technology estate before depreciation, and 60 percent said it had grown noticeably over the previous three years.
Someone measured what bad code actually costs
The "Code Red" study by Adam Tornhill and Markus Borg, presented at the TechDebt 2022 conference, is one of the few pieces of work that moves this argument from anecdote to data. They analyzed 39 proprietary production codebases across several domains, covering 30,737 files, combining static analysis with version-control history and Jira issue records. Low quality code contained 15 times more defects than high quality code. Resolving an issue in low quality code took on average 124% more time in development. Predictability suffered worst of all: maximum cycle times ran up to 9 times longer.
That last finding is the one that lands with a management audience. Being twice as slow is annoying. Having your estimates fail in unpredictable ways breaks planning outright. Fowler's take points the same direction, and it is more urgent than most teams assume. He argues the window in which cutting internal quality actually buys you speed is short, and that developers notice poor quality code slowing them down within a few weeks. Weeks, not months.
The "total debt" number from your scanner is not a budget
Static analysis tools report debt in days, and the figure looks persuasive until you check how it is produced. In the SQALE approach used by SonarQube, the technical debt ratio is the remediation cost divided by the development cost, where development cost defaults to 30 minutes per line of code. The maintainability rating comes out of that ratio, with an A requiring it to stay under 5%.
So "you have 142 days of debt" rests on a fixed constant and on how strictly your rule set happens to be tuned. It is a reasonable trend line and a poor budget line. Two rules keep it useful. Do not set a zero-issue target on legacy code; hold a hard threshold on newly written code instead, since that is the part you actually control. And measure the ratio on changed code rather than across the whole repository, because what the team produced this week tells you more than a total accumulated over five years.
Ranking work: where change frequency meets complexity
Not every bad file in a codebase costs the same. A complex module nobody has opened in two years is costing you nothing. The expense concentrates in files that are both complicated and touched constantly. That intersection is called a hotspot, and finding it does not require buying a tool. Your git history is enough:
git log --since="12 months ago" --name-only --pretty=format: \
| grep -v '^$' | sort | uniq -c | sort -rn | head -20
That gives you the twenty most frequently changed files of the past year. Line them up against file length or whatever complexity measure you already collect and your priority list is mostly written. The leverage here is real. In an example from CodeScene's documentation, the prioritized hotspots make up 5.5% of the codebase while accounting for 17.6% of development effort and 23% of all fixed bugs, and the docs note that the defect share tends to run higher than that in most codebases.
Timing follows the roadmap, not the ugliness
The question to ask is not "how bad is this file" but "are we going to touch it next quarter." If you are not, the debt sits there and charges you nothing. If you are, cleanup is the first step of that work, not a competing request. Kent Beck's rule compresses it into one line: for each desired change, make the change easy (warning: this may be hard), then make the easy change. Fowler calls the same move preparatory refactoring.
The practical consequence is that debt payments should not be negotiated separately. Two days of preparation folded into the estimate for a feature never gets argued about; a three-week refactoring request filed on its own gets deferred every single time. Keep the small continuous version too. Leaving each file you touch slightly cleaner than you found it adds up over a year to more than the scheduled cleanups ever deliver.
Budget: what percentage, and which items
The model that works is committing a fixed share of each iteration's capacity to maintenance and writing it down. The exact percentage matters less than it being stable and agreed with the product side. You already have the number that should inform it: the share of last quarter's time that went to unplanned work, meaning production bugs and emergency fixes. If that share is climbing, your maintenance allocation is too low.
Then there are crunch periods. A 2025 study examined 12.3k commits and 371 releases across eight open source projects using SonarQube and found that in some of them, more than half of releases showed technical debt accumulating as the deadline approached. The effect varied by project, but the direction was familiar. The only realistic defense is to log the debt you take on deliberately under pressure, with dates. Who decided, why, and which upcoming piece of work it has to be cleared before. Debt that never gets written down gets forgotten, and forgotten debt turns into "why on earth is this code like this" two years later.
AI is accelerating the accumulation
GitClear's 2026 maintainability research examined 623 million code changes between 2023 and 2026, and the signals all point one way. Duplicated code blocks rose 81%. Refactoring line moves fell 70%. Cross-file function calls, a proxy for reuse, dropped 35%. Copy-pasted code went from 9.4% of new code in 2022 to 15.7% in the first half of 2026. Constructs that mask errors rather than handle them increased 47%.
The mechanism is not mysterious. A model is very good at writing what you asked for and much less inclined to find and reuse what you already have, so you end up with five similar-but-not-identical implementations of the same thing. The countermeasures are old ones applied harder: make "does this already exist" a standard review question, reject generated code that quietly swallows errors, and if your team leans on AI assistants, regenerate the hotspot list monthly instead of quarterly.
What fits in this week
Three tasks. Run the git command above and list the twenty most-changed files. Put that list next to next quarter's roadmap and pick the two files that appear on both. Plan the cleanup of those two not as a standalone request but as a step folded into the estimate for the feature that needs them.
For the conversation with management, three numbers carry it: defect share in the hotspots, median time from merge to production for a change, and the percentage of capacity going to unplanned work. All three come out of data you already have. The simplest way to keep collecting them is to build the measurement into your delivery pipeline, which we covered in CI/CD for small teams. And when debt stops responding to file-by-file cleanup, the decision moves somewhere else; we worked through that one in rewrite or modernize incrementally.
Need help with this topic?