Leer en español · Lire en français · Auf Deutsch lesen
Section 1.7 of 1.8 · Intermediate · 20 min read
Technical debt, architecture erosion and drift
Video lesson
4 min · narrated, with captionsListen only (audio)
Read the transcript
Every team takes shortcuts, and every design ages as you learn more. This lesson explains technical debt: what it really means, how to see it, and how to decide what's worth repaying.
Imagine you move house in a hurry and throw everything into boxes without labels. You moved on time, which was the point. But now, every time you need something, you waste ten minutes searching. Those wasted minutes are interest. Unpacking and labelling properly is paying back the debt. A little of this is fine, and sometimes wise. The danger is letting it pile up.
Ward Cunningham coined the term in 1992. Shipping first-time code is like going into debt: a little speeds you up, as long as you pay it back promptly. He later explained that it was never a licence for sloppy code. His point was about understanding. You ship code that reflects what you know now, and as you learn more, you refactor it to match. That only works if the code is clean enough to refactor.
The financial words help. The principal is the effort needed to fix the problem. The interest is the extra effort you pay on every change that touches it. If a feature would take four days in clean code but takes six, the two extra days are interest. So debt in code that never changes costs nothing. Repaying is an investment decision: worth it when the interest you'll save is more than the principal.
Martin Fowler classifies debt on two axes: was it taken on deliberately or inadvertently, and was that reckless or prudent? Prudent and deliberate: we must ship now and deal with the consequences. It's conscious and planned, with a repayment plan, and it can be good business. Reckless and deliberate: we don't have time for design. The team knows better but cuts corners anyway, and usually slows itself down within weeks. Reckless and inadvertent: what's layering? The team can't see the debt it's creating. The cure is skills, review and mentoring, not refactoring alone. Prudent and inadvertent: now we know how we should have done it. Any team that learns ends up here, which is why continuous refactoring is normal work, not a sign of failure.
Code debt lives inside one module, and can often be repaid in hours or days. Architectural debt lives in the structure, such as services sharing a database. Repaying it touches many teams, and often data, so it's the most expensive kind. Perry and Wolf named two ways structure decays. Erosion comes from violations, such as a controller querying the database directly, bypassing the service layer. Drift comes from insensitivity: changes that break no rule but ignore the intended concepts, so the system gradually loses its shape.
How do you find the debt that matters? One technique plots each file by how often it changes and how complex it is. Complex files that rarely change cost little interest. Leave them alone for now. Simple files that change often are usually fine. But where complexity and frequent change coincide, you're paying interest on every commit. These hotspots are where repayment pays off first.
To make the case, an online shop measures the debt in a module that orders and payments share. It changed forty eight times in six months: the top hotspot. Extra effort on each change, plus incidents, comes to about fourteen developer-days a month. That's roughly eight thousand four hundred pounds. Separating orders from payments, in small steps, would cost about forty five developer-days, roughly twenty seven thousand pounds. With about eleven days saved each month, it pays back in around four months. Even doubling the cost, it pays back in about eight. That's a case in the sponsor's terms: days, money and payback, not code quality.
That's technical debt. The full section covers more ways to detect it, and how to manage it with a debt register, protected capacity and refactoring as you go. Next, in Section one point eight, we look at architecture across the lifecycle. See you there.
What you need to know
- Ward Cunningham coined the debt metaphor in 1992 to explain shipping code that reflects current understanding and then refactoring as understanding grows; it was never a licence for careless code.
- Principal is the cost of fixing the problem; interest is the extra cost paid on every change while it remains, so debt in code that never changes costs nothing.
- Fowler's quadrant classifies debt as deliberate or inadvertent and prudent or reckless; prudent debt is a normal by-product of delivering and learning, reckless debt is a skills or discipline problem.
- Architectural debt, in structure rather than code, is the most expensive kind because repaying it touches many modules, teams and often data.
- Perry and Wolf (1992) distinguished erosion, caused by violations of the architecture, from drift, caused by insensitivity to it.
- Hotspots (complex and frequently changed code), change coupling, dependency cycles and delivery trends make debt visible; a register, protected capacity and refactoring alongside features keep it under control.
What Ward Cunningham actually meant
The term technical debt comes from Ward Cunningham's experience report on the WyCash portfolio management system, presented at the OOPSLA conference in 1992. He wrote that shipping first-time code is like going into debt: a little debt speeds development, as long as it is paid back promptly with a rewrite, and the danger comes when it is not repaid: Every minute spent on not-quite-right code counts as interest on that debt.
Whole engineering organisations, he warned, can be brought to a standstill under the debt load.
The metaphor is often misread as permission to write sloppy code. Cunningham later explained (in a short 2009 video) that this was never his meaning. His point was about understanding. A team ships code that reflects its current, partial understanding of a problem, because that is the fastest way to learn from real use. As understanding improves, the code no longer matches what the team now knows, and every change has to work around the mismatch. Repaying the debt means refactoring the code so that it reflects what has been learnt. That only works if the code was written cleanly enough to be refactored in the first place. On this reading, debt is a by-product of learning, not of carelessness.
Since then, usage has broadened. Today "technical debt" covers any internal-quality shortfall that makes future change slower or riskier, whether it arose from learning, deliberate shortcuts, ignorance or plain neglect. The broader meaning is useful as long as you remember that the different kinds call for different responses.
Principal and interest
The financial vocabulary is what makes the metaphor powerful in conversations with non-engineers. The principal is the effort needed to fix the problem: to restructure the module, separate the database, remove the cycle. The interest is the extra effort paid on every piece of work that touches the problem while it remains. Martin Fowler's illustration: if a feature would take four days in clean code but takes six because of cruft, the two extra days are interest.
Three consequences follow. First, debt that is never touched costs nothing: a messy module that nobody changes accrues no interest, so repaying it may be wasted effort. Second, interest compounds: workarounds become foundations for more workarounds, new developers copy the patterns they see, and the cost of the eventual repair rises. Third, repayment is an investment decision: it is worth paying the principal when the interest you will save over a sensible horizon exceeds it. That turns an emotional argument ("the code is a mess") into an economic one ("this module costs us eleven days a quarter").
Fowler's technical debt quadrant
Not all debt is equal. In 2009 Martin Fowler proposed classifying debt along two axes: whether it was taken on deliberately or inadvertently, and whether the decision was prudent or reckless. The four combinations call for different responses.
Prudent and deliberate: the team knows the better design, judges that shipping now is worth the cost, and plans to repay. This is Cunningham's original sense, and it can be good business: a start-up proving demand before investing in scale. Reckless and deliberate: the team knows better but skips design because it believes it has no time, usually slowing itself down within weeks. Reckless and inadvertent: the team does not know enough to see that it is creating debt; the cure is skills, review and mentoring rather than refactoring alone. Prudent and inadvertent: a skilled team does good work and only later understands how it should have been structured. Fowler argues this is unavoidable for any team that learns, which is why continuous refactoring is part of normal work rather than a sign of failure.
The technical debt quadrant
Architectural debt versus code debt
Debt exists at every level, but its cost profile changes with scale. Code debt lives inside a module: duplicated logic, a function that does five things, missing tests, poor names. It is real, but it is local; one developer can usually repay it in hours or days, often while working on a nearby feature. Architectural debt lives in the structure: services that share a database so neither can change its schema alone, a domain split along the wrong lines so every feature touches four teams, a synchronous call chain that makes one slow service take down the checkout, a framework welded into every layer. Repaying it touches many modules, often many teams, and sometimes data.
Research supports the intuition that structural debt is the expensive kind. A 2015 survey of 1,831 engineers and architects by Neil Ernst and colleagues at the Software Engineering Institute found that architectural decisions were the most important source of technical debt reported, ahead of code-level issues. Tools that flag code smells are good at finding code debt and poor at finding architectural debt, because the problem is in relationships between parts rather than in any one file.
Architectural debt matters for the same reason architecture matters at all: as Why architecture matters showed, structural decisions are the ones with the highest cost of change.
Erosion and drift
Architecture debt often accumulates not through one bad decision but through thousands of small changes. Dewayne Perry and Alexander Wolf, in their 1992 paper "Foundations for the Study of Software Architecture", named two ways this happens. Architecture erosion is caused by violations of the architecture: a controller that queries the database directly, bypassing the service layer; a module that reaches into another's internal tables. Each violation makes the system more brittle, like removing a load-bearing wall. Architecture drift is caused by insensitivity to the architecture: changes that break no explicit rule but ignore the intended concepts, so the system gradually loses coherence and clarity of form. Drift is less immediately dangerous than erosion, but it obscures the architecture, which in turn makes violations easier.
Later researchers often use "erosion" for the broader gap between the intended architecture (what the documents and the architect's head say) and the implemented architecture (what the code actually does). Whatever the terms, the causes are familiar:
Deadline pressure that rewards the shortest path over the right path. Knowledge loss when the people who understood the design leave and new staff copy whatever they find. Invisible architecture: rules that live only in a wiki nobody reads, with nothing in the build to enforce them. Changed requirements, which leave a once-good structure fitting the problem badly. Organisational change: by Conway's law, a reorganisation creates pressure for the system to reshape itself along new team lines. Ageing technology: frameworks and platforms that fall out of support turn into debt without anyone touching the code.
Detecting debt
Debt you cannot see cannot be managed, and gut feeling is unreliable: developers tend to complain loudest about the code they touch most, which is useful, but they rarely know how much each problem costs. Several techniques make debt visible.
Hotspots. Adam Tornhill's Your Code as a Crime Scene (2015; second edition 2024) applies forensic ideas to version-control history. A hotspot is code that is both complex and changed often. Complexity alone is not enough: a complex file nobody touches costs no interest. Change frequency alone is not enough: a simple configuration file can change daily without harm. Where the two coincide, debt is being paid on every commit, and that is where repayment pays off first. In most codebases a small fraction of files accounts for most of the change activity.
Change coupling. Files or services that keep changing in the same commits are coupled, whether or not the code shows a dependency. If every change to the pricing service is accompanied by a change to the invoicing service, they are one thing pretending to be two. Change coupling across team or service boundaries is a strong sign of architectural debt.
Dependency cycles and rule violations. Static analysis can draw the real dependency graph and compare it with the intended one. Cycles (A uses B, B uses C, C uses A) mean none of the three can be understood, tested or released alone. Architecture rules ("the domain layer must not depend on the web layer") can be checked automatically on every build; these are fitness functions, covered in Chapter 11.
Delivery trends. Rising lead time for changes, more defects escaping per release, longer onboarding, more coordination meetings per feature and growing reluctance to touch certain areas are lagging indicators of accumulated interest. The DORA delivery metrics (such as change lead time and change fail rate) give a consistent way to track some of these over time.
Finding hotspots
Managing debt
No healthy codebase has zero debt, and trying to reach zero is itself a waste. The goal is to keep debt visible, deliberate and affordable. Teams that do this well combine several practices.
A debt register. Record significant debt items where the team plans its work: what the problem is, where it is, how it arose, the estimated principal, the estimated interest (with evidence such as hotspot data or recent tickets), and the trigger for repaying it. A register turns vague complaints into a list that can be prioritised alongside features.
A fixed share of capacity. Many teams reserve a steady proportion of each iteration, commonly somewhere between 10 and 20 per cent, for debt repayment and other improvement work. There is no evidence for a magic number; what matters is that the share is protected and does not disappear whenever a deadline looms.
Refactoring alongside features. The cheapest time to repay debt is when you are already working in the area, because you have the context loaded and the tests running. Fowler calls this opportunistic refactoring. Its everyday form is the boy scout rule, popularised by Robert C. Martin: leave the code a little cleaner than you found it. Because hotspots are by definition where features land, this naturally concentrates effort where interest is highest.
Planned structural repayment. Architectural debt rarely yields to small steps alone. It needs an explicit, funded piece of work with a goal, a plan for migrating in increments, and the support of stakeholders. Techniques such as the strangler fig pattern (Chapter 11, Modernising legacy systems) let a team replace a structure piece by piece without a big-bang rewrite.
Prevention. Automated architecture checks in the build, code review that looks at structure as well as style, and up-to-date decision records slow erosion and drift at source.
Explaining debt to non-technical stakeholders
Sponsors and product owners do not need to hear about cyclic dependencies; they need to understand the effect on what they care about: speed, cost, risk and customer experience. The financial metaphor is a gift here, provided you use it with numbers.
Talk in time and money: "Every change to checkout costs us about three extra developer-days because of how payment and orders are tangled together." Talk in risk: "Two of our last four incidents started in this module." Talk in options: "Until we separate these, we cannot offer the new payment methods sales has asked for." And present repayment as an investment with a payback period, not as a plea for engineering time. Avoid two traps: presenting debt as a moral failing of the people who came before (it is usually prudent or inadvertent), and asking for an open-ended "refactoring sprint" with no measurable outcome.
Worked example
Worked example: estimating the interest on a debt
An online shop's order and payment logic share one large module and the same database tables. The team suspects this costs them a lot. They gather evidence from the last six months of tickets and version-control history, then estimate.
| Changes touching the module | 48 changes in six months, about 8 per month (it is the top hotspot). |
|---|---|
| Extra effort per change | Comparing similar changes elsewhere, developers estimate about 1.5 extra days each for understanding, working around the shared tables and extra regression testing. |
| Effort interest | 8 changes × 1.5 days = 12 developer-days per month. |
| Defect interest | 3 production incidents in six months traced to this module, each costing about 4 developer-days to investigate and fix: 12 days ÷ 6 = 2 days per month. |
| Total interest | About 14 developer-days per month. At a loaded cost of £600 per developer-day, roughly £8,400 per month, or about £100,000 per year. |
| Principal | Separating orders from payments behind an interface and splitting the tables, done incrementally: estimated 45 developer-days, about £27,000. |
| Residual interest after repayment | Developers expect some overhead to remain: estimated 3 developer-days per month. |
| Payback | Monthly saving about 11 developer-days. 45 ÷ 11 is about 4 months to recover the principal. |
| Recommendation | Repay, in increments of about two weeks each, starting with the tables most often involved in incidents. Track lead time for changes to checkout to confirm the saving. |
Every number is an estimate with wide error bars. Doubling the principal to 90 days still gives payback in about eight months, so the conclusion is robust; that check is worth showing stakeholders.
The same method can show that some debt is not worth repaying: a messy module changed twice a year may have a payback period of several years.
The case is made in the sponsor's terms (days, money, incidents, payback), not in terms of code quality.
Check your understanding
Why can a very messy module be a low priority for repayment?
Because interest is paid only when the code is changed or causes failures. If nobody touches it and it is stable, the debt costs almost nothing.
Why is reckless, inadvertent debt not cured by refactoring alone?
Because the team did not know it was creating debt; unless its skills and review practices improve, it will keep creating the same kind of debt as fast as it repays it.
What is the difference between erosion and drift in Perry and Wolf's terms?
Erosion results from violating the architecture's rules; drift results from changes made without regard to the architecture that break no explicit rule but blur its concepts.
Why do hotspot analyses combine change frequency with complexity rather than use either alone?
Complex code that never changes accrues little interest, and simple code that changes often is cheap to change; only the combination shows where debt is costing effort repeatedly.
Sources and further reading
Have a question about this section? ArchitectBot, the course’s AI study assistant, answers from the course first.
Ask: “How do I persuade my product owner to spend time paying down architectural debt when there is a long feature backlog?”