Technical Debt Management
Status: Complete Category: Management Default enforcement: Soft Author: PushBackLog team
Tags
- Topic: management, quality, planning
- Skillset: management, engineering
- Technology: generic
- Stage: planning, execution
Summary
Technical debt is the accumulated cost of design and implementation decisions that optimise for short-term delivery at the expense of long-term maintainability. It is not inherently bad — deliberate technical debt taken consciously is a legitimate trade-off. Unmanaged technical debt, allowed to accumulate without visibility or plan, compounds interest until it dominates the cost of adding features and increases the risk of defects.
Rationale
Every software system accumulates technical debt. Teams that do not actively manage it are not avoiding it — they are accumulating it invisibly until it forces itself into visibility through slowdowns, outages, or inability to deliver.
Managed technical debt is a business conversation. Unmanaged technical debt is an operational surprise. Making debt visible, classifying it, and tracking paydown enables engineering teams to have honest conversations with product and business stakeholders about the cost of previous shortcuts and the value of remediation work.
Guidance
Debt taxonomy
Not all technical debt is the same. Classify debt to prioritise it:
| Type | Description | Urgency |
|---|---|---|
| Deliberate-reckless | ”We know this is wrong; we don’t have time to fix it properly” | High — fix ASAP |
| Deliberate-prudent | ”We’ll ship now and refactor when we understand this domain better” | Medium — schedule |
| Inadvertent-reckless | Introduced without knowing it was wrong (missing tests, poor naming) | Depends on impact |
| Inadvertent-prudent | Good design at the time; requirements evolved beyond it | Low — address opportunistically |
The Fowler/Cunningham quadrant provides this taxonomy. Understanding the origin of debt clarifies who owes the debt and whether it was a reasonable trade at the time.
The technical debt register
A technical debt register is a tracked list of known debt items with enough information to prioritise them. A minimal register entry:
| Field | Description |
|---|---|
| Title | Short description of the debt item |
| Location | Which component, service, or file is affected |
| Type | Deliberate / inadvertent, reckless / prudent |
| Impact | What problems does carrying this debt cause? |
| Effort | Rough estimate of paydown cost |
| Owner | Who is accountable for addressing it |
| Priority | Based on impact × carrying cost |
The register lives in the team’s backlog tooling, not in a spreadsheet that no one reads.
Paydown strategies
Debt paydown should be scheduled, not heroic:
- Boy Scout Rule — leave code better than you found it; address small debt items as part of feature work when the code is open
- Dedicated capacity — allocate a percentage of sprint capacity to debt work (10–20% is a common starting point)
- Debt sprints — periodic sprints focused on debt for systems where it has accumulated to a critical level
- Strangler fig — incrementally replace a problematic component behind an interface boundary rather than a “big bang” rewrite
Rewrites should be approached with caution. Full rewrites of large systems rarely deliver on their promise and introduce new debt while the old system is still running.
Making debt visible to stakeholders
Technical debt must be communicated in business terms:
- “This module has no tests; every change takes three times longer than comparable modules and releases have a 4× higher defect rate”
- “Our deployment pipeline takes 45 minutes; competitor teams deploy in 8 minutes; we lose approximately N developer-hours per sprint waiting for CI”
Frame debt as a carrying cost — an ongoing tax on delivery — rather than an abstract engineering concern.
Common failure modes
| Failure | Description |
|---|---|
| Invisible debt | No register; debt accumulates until it is discovered through incidents |
| Zero tolerance culture | All debt framed as shameful; teams hide it; no space to address it |
| Debt-only sprints that never happen | Team promises to “do a cleanup sprint next quarter”; it never arrives |
| Rewrites instead of paydown | Large rewrites attempted to escape debt; new system reintroduces it |
| No stakeholder visibility | Engineering knows about the debt; product and business do not; prioritisation battles are lost |
Examples
Tech-debt register entry
## TD-047 — Authentication service uses deprecated `jsonwebtoken` v8 signing API
**Category**: Dependency / Security
**Discovered**: 2024-08-12
**Severity**: High
**Estimated effort**: 1 day
**Symptom**
The auth service uses `jwt.sign(payload, secret, callback)` with the v8 callback API. `jsonwebtoken` v9 removes this form; the library also had two CVEs (CVE-2022-23539, CVE-2022-23541) patched only in v9+.
**Impact if left**
- Known JWT validation vulnerabilities remain in production
- Upgrading past v8 later will be a larger breaking change as other callers may accumulate
**Proposed resolution**
Upgrade `jsonwebtoken` to `^9.0.0`; migrate two call sites from callback to synchronous `jwt.sign()`; update unit tests.
**Owner**: @todd
**Target sprint**: Sprint 28
Linking debt to work: the “boy scout” pattern in a PR
## What this PR does
Refactors the invoice service to use the repository pattern.
## Debt addressed
This resolves TD-031 (service layer directly querying the database, making unit tests impossible without a live DB). As per team norms, the refactor is scoped to the files changed for the feature — no unrelated files changed.
## Debt introduced
None introduced. The migration script in `scripts/migrate-invoices.ts` is deleted after use; no new long-term debt.
Debt ratio in sprint planning
Sprint 28 capacity: 40 points
Allocation:
Features / stories 32 points (80%)
Debt repayment 8 points (20%)
└ TD-047: jsonwebtoken upgrade 2 pts
└ TD-038: Remove feature flag residue 3 pts
└ TD-029: Replace hand-rolled CSV parser 3 pts
Debt register before sprint: 27 items
Debt register after sprint (projected): 24 items
Maintaining the 20% floor means the register shrinks over time even as normal delivery introduces new items.
Related practices
Part of the PushBackLog Best Practices Library. Suggest improvements →