Six Sources of Technical Debt (And What to Do About Each)
Updated 17 April 2026
Every piece of technical debt traces back to one of six systemic sources. Understanding the source is the prerequisite for choosing the right remediation. The same codebase pattern can come from a rushed deadline (fix: process), a departing expert (fix: documentation), or a premature abstraction (fix: simplification) -- three completely different responses.
For comparison: rework costs arise from the same sources. See reworkcost.com on unclear-requirements rework for the sibling treatment.
Source 01
Rushed Deadlines
The most common source of technical debt across all team sizes. External schedule pressure forces shortcuts that become permanent defaults.
Mechanism
When a deadline is immovable and scope cannot be reduced, engineers make quality trade-offs. Tests are skipped. Abstractions are simplified. Documentation is deferred. The problem is not the trade-off itself -- Fowler's Deliberate+Prudent quadrant validates this -- but that the cleanup is rarely scheduled with the same discipline as the original feature.
Evidence
The Standish Group CHAOS Report (2020) found that 29% of IT projects succeed on time and on budget without cutting scope; the majority make quality concessions. DORA's State of DevOps 2024 found a strong negative correlation between time pressure scores and software delivery performance. Teams that report high deadline pressure have 2.4x higher change failure rates than low-pressure teams.
Named Example
A feature team shipping an MVP for a board demo in three days skips integration tests, hardcodes a configuration value, and uses a synchronous API call where async is needed. The MVP ships, the board approves. The cleanup tickets are never prioritised because Q2 brings a new set of deadlines.
Remediation
Buffer the schedule (reserve 20% for quality). Renegotiate scope rather than quality. Ship narrower features done properly rather than broader features done poorly. Create the cleanup tickets at the same time as the feature tickets and give them the same sprint priority.
20% sprint allocation lever +Source 02
Legacy Migrations
Code that was written under earlier norms, patterns, and constraints that is now out of step with the team's current practices and tools.
Mechanism
Legacy code is not bad code -- it is code that reflects the knowledge and constraints of when it was written. The debt accumulates when the surrounding context changes (new language versions, new patterns, new team norms) and the existing code does not migrate with it. The interest rate is a function of how often the code is touched.
Evidence
Dropbox's Python 3 migration ran from 2014 to 2020 -- seven years to migrate a single codebase from Python 2 to Python 3. Dropbox published detailed post-mortems explaining the complexity. GitHub's Rails monolith migration is ongoing. The 17.3 hours per week figure from Stripe's 2018 Developer Coefficient is primarily attributable to maintenance of legacy systems.
Named Example
A company was founded in 2010 using jQuery and server-side rendering. The team has moved to React and REST APIs, but the customer-facing portal still runs the original jQuery stack. Every new feature requires shipping two implementations: one in React for the new interface, one in jQuery for the portal. The portal has 40,000 users who have not migrated.
Remediation
Strangler fig pattern: build the new implementation alongside the old, route traffic incrementally, deprecate the old when traffic is migrated. Architecture Decision Records to prevent the same pattern recurring. Set a sunset date and hold to it.
Strangler fig pattern +Source 03
Dependency Drift
Transitive dependencies update, APIs change, breaking versions accumulate. The codebase falls behind the ecosystem it depends on.
Mechanism
Open source libraries release breaking changes. Cloud provider APIs deprecate. Language runtimes go end-of-life. Each deferred update increases the upgrade complexity: going from version 3 to version 5 is exponentially harder than 3 to 4 followed by 4 to 5, because each intermediate version introduces different breaking changes.
Evidence
Snyk's State of Open Source Security 2024 found that 84% of enterprise codebases contain at least one known vulnerability in their open source dependencies. The Log4Shell vulnerability (CVE-2021-44228, CVSS 10.0) exposed the severity: applications that had drifted three major versions behind could not apply the patch without also migrating other parts of the stack.
Named Example
A Node.js application running version 14 (end-of-life since April 2023) with 200 npm dependencies. The team has deferred the upgrade because the last time they tried, 23 packages broke. The upgrade project is estimated at 4 engineer-weeks. There are currently 7 known critical CVEs in the dependency tree.
Remediation
Dependabot or Renovate for automated dependency PR creation. Software Bill of Materials (SBOM) for auditability. Quarterly dependency-update sprints, never skipped. Strict policy: no dependency more than one major version behind the current release.
Automated quality gates +Source 04
Expertise Loss
Senior engineers leave. Tribal knowledge evaporates. The codebase becomes opaque to everyone who remains.
Mechanism
Adam Tornhill's research (CodeScene, 2015-2024) on truck factor shows that most codebases have significant knowledge concentration: a small number of engineers hold almost all the knowledge for high-complexity, high-churn modules. When those engineers leave, the effective interest rate on that code spikes. Every change takes 2-5x longer for the remaining team.
Evidence
Tornhill's analysis of open source projects found that 80% of the knowledge of a typical codebase is concentrated in 10-20% of contributors. MIT Sloan Management Review documented the 'code halflife' phenomenon: the average lifespan of knowledge about a specific module before the engineer who wrote it has left is 3-5 years for rapidly scaling teams.
Named Example
The authentication service was written by a senior engineer who left 18 months ago. She is the only person who understood why the session token uses a custom HMAC rather than a standard JWT. The service has 12 open issues, 6 of which are security-related. Nobody wants to touch it because nobody understands it.
Remediation
Architecture Decision Records written at time of decision (not retroactively). Pair programming rotation: all complex modules should have at least two engineers with deep familiarity. Documentation-as-code in the same repo as the service. Quarterly 'archaeology sessions': senior engineers walk junior engineers through historical decisions.
Architecture Decision Records +Source 05
M&A Integration
Acquired company's stack differs from the acquirer's. Two codebases, two philosophies, one integration deadline.
Mechanism
M&A due diligence rarely includes a rigorous technical debt assessment. The business team agrees the deal; the engineering team inherits a foreign codebase with unknown debt, different conventions, and a hard integration deadline. The resulting systems are often deeply coupled but architecturally incompatible -- held together by integration layers that nobody fully understands.
Evidence
Post-acquisition technical integration is consistently cited as one of the most expensive and time-consuming aspects of technology M&A. Cisco has published multiple case studies on integration complexity. The Oracle/Sun Microsystems acquisition involved a Java codebase migration that lasted years and required hundreds of engineering-years.
Named Example
A Node.js-native SaaS acquires a competitor whose core product is a Ruby on Rails monolith. The acquirer's platform team has zero Rails expertise. The acquired company's team of eight engineers has signed 12-month retention packages. At month 13, five of the eight leave. The Rails system processes 30% of revenue. Nobody on the acquiring team can modify it safely.
Remediation
Two-track strategy: run the acquired system in parallel while building a strangler replacement. Or: deliberate sunset with a customer migration programme. Either way, write ADRs immediately for all inherited architectural decisions. Never attempt a big-bang integration.
Strangler fig pattern +Source 06
Premature Abstraction
Engineers abstract for imagined future needs. Creating flexibility nobody uses. The resulting code is harder to understand and modify than a simple direct implementation.
Mechanism
Sandi Metz's rule: 'duplication is far cheaper than the wrong abstraction.' Premature abstraction is the most insidious form of inadvertent debt because it looks like good engineering at the time. The abstract is usually written by the most senior engineer. It gets used everywhere. The imagined use-cases never materialise. The abstraction calcifies and becomes a constraint.
Evidence
John Ousterhout's 'A Philosophy of Software Design' (2018) documents complexity created by over-engineering: 'If a system has many layers of abstraction, the programmer must understand all of them to understand the system.' Ousterhout specifically calls out the 'classitis' anti-pattern: systems with hundreds of tiny classes that are hard to understand in isolation.
Named Example
A plugin architecture built for a system that currently has two plugins and will likely never have more than five. The architecture supports an arbitrary number of dynamically loaded plugins with versioned interfaces and dependency injection. It took six weeks to build. Understanding it takes a full day. The two plugins exist. Nobody is building a third.
Remediation
Rule of three: do not abstract until you have three concrete cases. Inline the abstraction if it has fewer real consumers than layers. Ousterhout's principle: prefer deep modules (small interface, lots of functionality) over shallow modules (large interface, little functionality).
Boy-scout rule +Continue reading:
Sources
- Standish Group. CHAOS Report 2020. Standish Group International.
- Google DORA. State of DevOps Report 2024.
- Stripe. The Developer Coefficient. 2018.
- Snyk. State of Open Source Security 2024.
- Tornhill, A. Your Code as a Crime Scene. Pragmatic Bookshelf, 2015.
- Ousterhout, J. A Philosophy of Software Design. Yaknyam Press, 2018.
- Metz, S. Practical Object-Oriented Design in Ruby. Addison-Wesley, 2018.