Verdict up front: Stop arguing about tech debt with vibes. Measure it (SonarQube technical debt ratio is one good metric: total remediation effort / total development effort). Prioritise by interest rate: high-change-frequency code with high debt = highest interest = fix first. Aim for under 10% TD ratio and 20% of engineering time allocated to debt reduction.
The interest rate analogy
Tech debt works like financial debt. When you take shortcuts to ship faster, you accumulate principal. The "interest" you pay is the extra time every future change in that code takes.
Some debt has high interest (charged frequently): a messy auth system you touch every sprint costs you 30% extra time on every feature. Some debt has low interest: a clunky internal reporting script you touch twice a year barely costs you anything.
Refactoring is "paying off the principal." The question isn't "should I refactor?" — it's "which debt has the highest interest rate?"
Measuring tech debt: the SonarQube approach
SonarQube calculates technical debt ratio as:
TD ratio = (remediation cost in hours) / (development cost in hours) × 100%
Where:
- Remediation cost: SonarQube estimates the effort to fix all detected code smells based on rule severity
- Development cost: Estimated using lines of code × cost per line (default 0.06 hours/LOC)
Industry benchmarks:
- Under 5%: Excellent (uncommon)
- 5-10%: Healthy
- 10-20%: Concerning, plan refactoring
- 20-40%: Paying significant interest on every change
- Over 40%: Consider rewrite or modular extraction
Real numbers from teams we've audited: most teams discover 15-30% TD ratio when first measured. The shock is healthy — it makes "we have some tech debt" concrete.
Free metrics you can compute today
No need for SonarQube to start. Useful proxies:
Code volatility
Files modified most often in the last 90 days. These are high-interest if they're also messy.
git log --since="90 days ago" --name-only --pretty=format: \
| sort | uniq -c | sort -rn | head -20
Cross-reference with code complexity. A file in the top 20 by volatility AND top 20 by complexity = highest priority.
Test coverage gap
Files without corresponding test files = highest risk per change.
# Python example
find . -name "*.py" -not -path "*/test*" \
| xargs -I{} sh -c 'test -f "tests/test_$(basename {})" || echo "{}"'
Longest functions
Functions over 50 lines are debt magnets. Find them:
# Python
pip install --user radon
radon cc -nc -s -a .
Anything with cyclomatic complexity over 15 is a refactor candidate.
Dependency age
# Node
npm outdated --long
# Python
pip list --outdated
Packages more than 2 major versions behind = security and compatibility debt.
Build time
If your CI takes 30+ minutes, you're paying interest every PR. Track build time over time — if it's growing, debt is accumulating.
The prioritisation matrix
Score each piece of debt on three axes:
| Axis | 1 (low) | 5 (high) |
|---|---|---|
| Effort to fix | 1-2 days | 1-2 months |
| Change frequency (how often this code is modified) | Rarely (once/quarter) | Often (multiple times/week) |
| Risk if untouched (bugs, security, performance) | Cosmetic | Production incidents |
Priority = (Change frequency × Risk) / Effort
Highest scores: high-change + high-risk + low-effort. Fix these first.
Lowest scores: rarely-touched + low-risk + high-effort. Leave alone, possibly forever.
Side-by-side: debt types
| Debt type | Interest rate | Fix priority |
|---|---|---|
| Slow CI/CD pipeline (30+ min builds) | Very high | Top priority |
| No tests on critical auth/payment code | Very high | Top priority |
| Outdated dependencies with CVEs | Very high (security) | Top priority |
| Monolithic file in active development | High | Schedule refactor |
| Inconsistent code style | Medium | Auto-fix with linter |
| Outdated dependencies, no CVEs | Low | Update during minor work |
| Old comments, dead code in cold paths | Very low | Defer indefinitely |
| Old framework version (still supported) | Low | Plan for major refactor cycle |
| Stale documentation | Medium (onboarding cost) | Update as you touch code |
The 20% rule
Industry consensus: allocate 20% of engineering time to debt reduction. Practical implementations:
- Friday afternoon refactor: Each developer spends Friday afternoon on tech debt of their choosing.
- Tech debt sprint: Every 4th sprint is dedicated to debt. Strong commitment, easier to defend to product.
- Boy scout rule: Leave code cleaner than you found it. Each PR fixes one small issue. Compounds over time.
- Debt budget per feature: Each feature PR has 20% headroom for adjacent cleanup. Embedded in normal work.
The boy scout rule sounds gentle but is the most effective. A team of 5 developers fixing 1 small thing per PR = 50+ small fixes per month. Compounded over a year, that's transformative.
When to declare bankruptcy
Sometimes refactoring is more expensive than rewriting. Signs you should consider a rewrite over incremental refactor:
- TD ratio over 40% on a core system
- Every feature takes 3-5x longer than estimated
- New hires take 6+ months to be productive
- Core dependencies are EOL (Python 2, Node 12, Rails 4)
- You can't deploy to production without manual steps
- Your build/test cycle is 2+ hours
Even then, prefer strangler-fig over rewrite-from-scratch. Build the new system alongside the old, route traffic gradually, retire old code in chunks.
Cost of NOT measuring
Unmeasured tech debt:
- Estimates miss by 2-3x consistently (you're paying interest you can't see)
- Engineers burn out fixing the same fires repeatedly
- New hires struggle to onboard (the "tribal knowledge" tax)
- Product team thinks engineering is slow (engineering can't articulate why)
Measured tech debt:
- You can show product "this feature is 50% slower because of debt in module X"
- You can prioritise refactor work against feature work with data
- You can track whether you're getting better or worse over time
- You can justify the 20% allocation to skeptical stakeholders
Tools worth using
- SonarQube / SonarCloud: Free for open source, €11/month/user for private. TD ratio + code smells.
- CodeClimate: Similar to SonarQube, simpler UI, $20/seat/month.
- Code Scene: Behavioural analysis (which files are hotspots based on commit history). Open source + paid SaaS.
- Snyk: Dependency vulnerability tracking. Free tier covers most needs.
- Dependabot: Free, automatic dependency updates. GitHub-native.
Tech debt blocking your roadmap?
We audit codebases, quantify tech debt in hours, and produce a prioritised refactoring plan. Typical 50k-line codebase audit: 1 week, €4-6k.
Book a discovery call