Technical debt is the hidden tax on every software product. Unlike financial debt, it doesn't appear on any balance sheet — but it accumulates interest every single sprint, showing up as slower releases, more bugs, and frustrated engineers. The McKinsey Technology Institute estimates that technical debt accounts for 20–40% of the value of technology assets before depreciation.
What Is Technical Debt, Really?
Ward Cunningham, who coined the term, defined it as "the not-quite-right code which we postpone making it right." But it's broader than that. Technical debt includes outdated dependencies, missing tests, poorly documented APIs, over-engineered abstractions, and architectural decisions that made sense in 2019 but are now bottlenecks. Not all debt is bad — sometimes you deliberately incur it to ship faster. The problem is untracked, unintentional debt that compounds over time.
The True Cost: A Framework
- <strong>Developer velocity loss.</strong> Teams in high-debt codebases ship 25–50% slower than those in clean codebases, per DORA Research.
- <strong>Increased bug rate.</strong> Complex, untested code has 3–5× more defects per line than well-structured code.
- <strong>Onboarding friction.</strong> New hires in high-debt codebases take 2–3× longer to become productive.
- <strong>Opportunity cost.</strong> Every hour spent working around debt is an hour not spent on features that generate revenue.
- <strong>Security exposure.</strong> Outdated dependencies are the #1 source of CVEs in production applications.
How to Measure Your Debt Load
Before you can fix debt, you need to see it. Start with these four metrics: (1) Cyclomatic complexity — flag any function with complexity > 10. (2) Test coverage — anything below 60% is a red zone. (3) Dependency age — check for packages more than 2 major versions behind. (4) Mean time to ship a feature — track this over 6 months; a rising trend is a debt signal.
# Check for outdated npm packages
npm outdated
# Find high-complexity functions (requires eslint-plugin-complexity)
npx eslint src --rule '{complexity: [warn, 10]}'
# Test coverage report
npx jest --coverage --coverageReporters=text-summary
The 20% Rule: A Practical Repayment Strategy
The most sustainable approach is the "20% rule": allocate 20% of each sprint to debt reduction. This keeps debt from compounding without grinding feature development to a halt. Prioritise debt that sits on the critical path of your most-changed files — that's where the velocity drag is highest.
When to Do a Full Rewrite
A full rewrite is warranted when the cost of maintaining the current codebase exceeds the cost of rebuilding it over a 2-year horizon. Signs you're there: your senior engineers refuse to touch certain modules, your deployment pipeline takes more than 45 minutes, or you've had more than three P1 incidents in a quarter caused by the same architectural weakness.
Never do a "big bang" rewrite. The Strangler Fig pattern — incrementally replacing modules while keeping the system running — has a dramatically higher success rate. Rewrites that try to replace everything at once fail more than 60% of the time.
Got a project in mind?
I work directly with founders and CTOs to build reliable, scalable software. Let's have a conversation about your goals.
Get a Quote