Debt Identification
Surface the shortcuts, workarounds, and decay in your codebase before they become emergencies.
Why
Technical debt is invisible until it isn't — a shortcut taken in sprint 3 becomes the 2-day debugging session in sprint 20. Identification makes the invisible visible: you catalog the TODOs, the duplicated logic, the untested paths, the framework version two majors behind, and suddenly you have a list you can manage instead of a vague dread. The goal isn't zero debt (that's impossible and undesirable — debt is a tool for speed) but known debt with a known cost. Teams that don't identify debt are always surprised by estimates that blow up, because the debt is the reason a "simple" feature takes a week, and nobody connected the two.
When
Continuously — tag debt in code as you create it (TODO(debt):), and run a quarterly inventory to surface what's accumulated.
How
1. Tag debt at creation: every shortcut gets a TODO(debt): <reason> comment with a ticket link 2. Run static analysis: eslint (complexity, duplication), knip (dead code), dependency-cruiser (architecture) 3. Search for debt markers: grep -rn "TODO(debt)|FIXME|HACK|XXX" src/ 4. Catalog in a DEBT.md or issue tracker: each item has type, location, estimated cost, interest rate (how fast it compounds) 5. Classify: code debt (duplication, complexity) | design debt (coupling, missing abstraction) | test debt (untested paths) | dep debt (outdated) 6. Measure: track cyclomatic complexity, duplication %, test coverage, outdated-dep count as trend lines 7. Review the catalog quarterly; items that haven't been touched in 2 quarters are candidates for explicit acceptance
Example
# Tag debt at creation
// TODO(debt): duplicated validation logic — extract to shared schema
// Ticket: https://tracker/issues/DEBT-42
function validateOrder(o) { /* copied from validateUser */ }
# Quarterly inventory script
grep -rn "TODO(debt)|FIXME|HACK" src/ > debt-raw.txt
npx eslint --rule '{"complexity":["error",15],"no-dupe-keys":"error"}' src/
npx knip --include dependencies,files,exports
npx dependency-cruiser --validate .dependency-cruiser.js src/
# DEBT.md catalog
| ID | Type | Location | Cost | Interest | Age |
|-----|--------|-----------------|-------|----------|------|
| D1 | code | order.ts:dup | 2h | high | 3mo |
| D2 | test | payment.ts:0cov | 4h | critical | 6mo |
| D3 | dep | react@16 | 2d | high | 9mo |Common Problems
- ⚠Untagged debt — a shortcut taken without a TODO means nobody knows it's a shortcut, so it's treated as the design and never paid.
- ⚠Measuring only code debt (complexity) and ignoring test debt — untested payment code is higher-risk than a complex utility.
- ⚠The debt catalog exists but is never reviewed — it grows to 500 items and becomes a guilt list, not an action list.
Best Practices
- ✓Tag every shortcut with TODO(debt): + a ticket at creation time — debt created without a tag is debt you'll forget you owe.
- ✓Track debt as trend lines (complexity, duplication %, outdated deps), not just a static list — a flat or falling line is the goal.
- ✓Review the debt catalog quarterly and explicitly accept items older than 2 quarters — acceptance is a valid outcome, denial is not.