31 May 2026 (updated: 31 May 2026)
Chapters
If you are deciding whether to refactor a legacy application now or push it down the roadmap, refactor now, in small and continuous slices. Deferred technical debt does not sit still. It accrues interest. Every shortcut you keep makes the next change slower, riskier, and more expensive, until a one-week feature turns into a one-month negotiation with code nobody fully understands. The cheapest debt to carry is the debt you pay down a little at a time, inside the work you are already doing.
Executive Summary:
Technical debt now consumes between 21% and 40% of most organizations' IT spending, according to Deloitte's 2026 Global Technology Leadership Study. Pegasystems estimates the average global enterprise wastes more than $370 million a year on the friction of outdated systems, with stalled modernization projects alone accounting for roughly $134 million of that.
The companies that account for debt honestly project higher returns than the ones that ignore it, and in 2026 the gap is widening because AI coding tools generate code faster than teams can review it. This article covers how to decide what to refactor, which patterns ship debt down without stopping delivery, and how to make the business case in language a CFO will sign off on.
The analogy to money is more than a metaphor. When a company is acquired, the buyer reads the balance sheet to understand its liabilities. Technical debt is a liability too, but it never shows up as a line item. IBM's analysis describes the same compounding dynamic that applies to a loan: each shortcut taken today makes future work slower and riskier, which creates pressure for more shortcuts, which creates more mess. The interest payment is real engineering hours, paid every sprint.
The damage rarely stays inside the codebase. As systems get harder to change, engineers start avoiding the scary parts. New code gets layered on top of old code with side effects nobody predicts. Bugs multiply, and some of them turn into security holes. People who understood the original design leave, and their knowledge leaves with them. Onboarding gets slower, documentation rots, and the team spends its days firefighting instead of building. That is the part stakeholders underestimate: debt is not only a code problem, it is a retention and velocity problem that quietly raises the cost of everything you ship.
The same tools that promise to clear debt are also accelerating it. GitClear's review of 211 million changed lines of code found that as AI assistants spread, copy-paste patterns rose sharply, code duplication climbed from 8.3% to 12.3% of changed lines, and refactoring activity collapsed from about a quarter of changes to under a tenth. Developers leaning on generation tend to add new code rather than reshape what already exists, which is the exact behavior that builds debt.
There is a budget dimension too. Gartner predicts that by 2027, 40% of enterprises using consumption-priced AI coding tools will face unplanned costs exceeding twice what they expected. A "the model will handle it" mindset defers real design decisions to a system that has no view of your architecture, and the result is under-designed code that behaves unpredictably once it meets production.
AI cuts both ways, though, and that is the useful part. The same technology that floods a repo with duplication is good at the grunt work of paying debt down. It can generate tests that pin current behavior before you change anything, document what a legacy function actually does, de-duplicate redundant code, and map dependencies across a large system to suggest an upgrade path. Used deliberately, AI lowers the cost of the maintenance nobody wants to do by hand. Used passively, it raises it. The difference is whether a human is steering toward smaller, clearer code or just accepting whatever the model emits.
Not all old code is debt, and chasing every ugly file is how teams burn goodwill. A senior engineer in this r/ExperiencedDevs discussion described a heat-map approach that holds up well: cross-reference how often a module changes (from git history) against how often it breaks (bug density and deployment failures). The overlap of "changes often" and "breaks often" is your highest-value target. Code that is stable and rarely touched can stay ugly forever, because ugliness only costs you when you have to read or modify it.
The honest test is whether the gains outweigh the cost. If they do, you have debt worth paying. If they do not, you just have old code, and old code that works and nobody touches is not an emergency. Prioritize critical paths and the parts you keep returning to. A module untouched for two years is usually fine to ignore. A module you edit every sprint needs to be pleasant to work in, because you are paying its tax repeatedly.
The failure mode to avoid is the big-bang refactor living on a long-running branch. Those branches kill morale and rarely merge cleanly. Incremental, feature-flagged change that ships with every sprint is far more sustainable. Two patterns make that possible.
The strangler fig pattern, named by Martin Fowler after the plant that grows around a host tree and gradually replaces it, lets you build the new system alongside the old one instead of rewriting in place. A routing layer such as an API gateway sits in front and directs traffic to either the legacy or the modern component, so users notice nothing while you migrate piece by piece. Microsoft's Azure Architecture Center documents the same approach. The discipline that makes it work is slicing thin: replace one capability at a time, small enough to ship safely but meaningful enough to deliver business value, until the old system can finally be retired.
For everyday work, Kent Beck's Tidy First? offers a smaller, faster discipline. Before you change behavior, make a quick structural change that makes the behavior change easier. These tidyings take minutes to a few hours, and you fold them into your estimate rather than asking permission. If a feature takes 20% longer because you cleaned up the code path first, you eat that 20% and the platform gets a little more stable each time. When the code is too tangled to tidy up front, you make the change first and tidy after, keeping the cleanup proportionate to the change.
Stakeholders rarely respond to "the code is messy." They respond to cost and risk. Two metrics travel well from engineering into a boardroom: cycle time per module and incident frequency per module. "This part of the system takes three times longer to change and causes 60% of our P1 incidents" is a business statement, not a technical complaint. Frame the work as risk reduction rather than cleanup, because risk is a language leadership already speaks.
Timing matters as much as framing. When you scope a feature that touches a debt-heavy area, name the cost up front: this takes two weeks as-is, or two weeks with modernization that reduces the time and risk of every future change here. Folding the case into a specific decision beats asking for an abstract refactoring sprint that competes against visible features and loses. And push for real time, not a token day every two weeks. A focused sprint here and there, with numbers to show the payoff, builds the trust that makes the next one an easier sell.
|
Factor |
Refactor incrementally now |
Defer the work |
|---|---|---|
|
Upfront cost |
Small, absorbed into normal sprints |
Zero today, larger and lumpier later |
|
Delivery risk |
Low; feature-flagged, reversible slices |
High; rises as code accumulates side effects |
|
Team morale |
Improves; engineers shape code they own |
Erodes; firefighting and fear of touching code |
|
ROI trajectory |
Compounds upward as cycle time drops |
Declines 18%–29% when debt is ignored (IBM) |
|
AI-debt exposure |
Contained; tests and review gate generated code |
Amplified; duplication and churn compound |
|
Knowledge retention |
Preserved; understanding stays current |
Lost as senior engineers leave |
Most teams know they should be doing this. What they lack is the time to run the analysis and the outside pressure to protect refactoring work from being deprioritized. That is the mechanism a modernization engagement provides, and it follows a clear chain from activity to outcome.
The work starts with an assessment: pulling git history and incident data to build the change-frequency-versus-bug-density heat map, so the first refactors target the modules that actually cost you money. That assessment becomes a prioritized, incremental backlog of strangler-fig slices and tidy-first opportunities, sized to ship inside existing sprints rather than as a separate project that stalls. The behavior change is the part that lasts: teams adopt continuous debt management as a habit, gated by tests and review so AI-generated code does not quietly reintroduce the problem. The outcome shows up in the metrics that move a business case, namely faster cycle time, fewer P1 incidents, and the retained ROI that IBM ties to accounting for debt honestly.
This only works under real conditions, and it is worth being direct about them. Leadership has to fund sustained effort, not a symbolic day per fortnight, or the cleanup never reaches escape velocity. You need a baseline of tests before refactoring a critical path, because changing code you cannot verify is how you turn debt into an outage. Modules need clear owners, since debt thrives where no single team feels accountable. And the heat map depends on access to git history and incident records being available and trustworthy. Where those conditions hold, the compounding runs in your favor instead of against you.
The teams that stay ahead of debt stop treating it as a project with a start and end date and start treating it as a property of how they work. Continuous assessment, prioritized by where change and failure actually overlap, feeds a steady stream of small refactors that ship with normal delivery. Tests and review keep AI-generated code from refilling the bucket. The compounding that works against you when you wait is the same compounding that works for you when you pay down a little every sprint. Build the system that does that, and the cost of waiting stops being a bill you are afraid to open.
Is all old code technical debt?
No. Debt is code whose cost to keep outweighs the cost to fix, usually because you change it often or it breaks often. Old code that is stable and untouched carries little ongoing cost, so it is legacy code rather than debt. Spend your effort where the interest payments are real.
How do I convince non-technical stakeholders to fund refactoring?
Translate the problem into cycle time and incident frequency per module, then attach money or risk to it. A statement like "this module causes most of our severity-one incidents and takes three times longer to change" lands as a business case. Tie the request to a specific upcoming feature so it competes on concrete value rather than as an abstract chore.
Does AI-generated code count as technical debt?
It can, and increasingly it does. GitClear's data shows AI assistance correlates with more duplication and less refactoring, which is the signature of accumulating debt. The code is not doomed, but it needs the same gates as any other contribution: tests that pin behavior, human review, and a bias toward reshaping existing code instead of pasting new variants.
Should I refactor or rewrite from scratch?
Default to incremental refactoring, usually with a strangler fig approach, because full rewrites carry high risk and often fail to ship. A rewrite makes sense only when the platform genuinely cannot support new requirements and the cost of running the old and new systems in parallel is justified. Even then, migrate in thin slices rather than flipping a switch.
How do I measure technical debt without a dedicated tool?
Start with data you already have. Git history gives you change frequency, your issue tracker gives you bug density, and your deployment logs give you change failure rate. Cross-reference them to find the modules that change and break the most. Code-level signals like cyclomatic complexity and duplication add detail, but the change-and-break heat map alone is enough to set priorities.
31 May 2026 • EL Passion