Tech Debt Register for Small Teams
Tech debt is not a moral failing; it is a loan. Every shortcut you shipped bought speed that day and has been charging interest ever since — rework on every change that touches it, minutes added to every deploy, one more way a 2am page can happen. A register is not a shame list. It is the amortization table: which loans you are still paying interest on, who holds each one, and what event forces repayment. One screen, ten rows. If it does not fit on one screen, you are not tracking debt — you are archiving guilt.
The register: one screen, ten rows, six fields
One row per debt. Six fields, no more:
- What — one sentence, in code terms a new hire could find: "payments retry logic duplicated across three services."
- Where — repo, module, or both. A location, not a vibe.
- Interest — how the debt charges you, in units you already track: hours of rework per month, minutes added to deploys, incidents attributable to it. This column decides priority — two debts of identical size can charge wildly different interest.
- Owner — a name. "The team" owns nothing.
- Repayment trigger — the event that forces paydown: "next feature that touches payments," "third on-call engineer joins," "next dependency upgrade of X." Debt is repaid when the trigger fires — not when a refactor sprint magically appears.
- Size — honest hours or days. Not story points; a size is for scheduling, an interest is for priority.
Ten rows visible, maximum. The eleventh row goes to an archive section — not because it does not matter, but because a register nobody can read in one glance is a graveyard, and graveyards do not get paid.
The interest column is the whole point
Size alone tells you nothing: a two-day debt charging four rework hours a month outranks a two-week debt charging nothing because nobody touches that module anymore. Measure interest in evidence the team already produces — rework hours from the last month, added deploy minutes from the last ten deploys, incident count the post-mortems attribute to it. If you cannot name the interest, the row is a preference, not a debt — and preferences live in the backlog, not the register.
Paydown rides feature work, on trigger
The register does not schedule a refactoring quarter. When a row's trigger fires — the next feature touches that module anyway — the debt is paid inside the feature PR, expand-then-contract style: additive module or interface with parity tests first, delete the old path in the follow-up PR. The person who touches the debt pays the interest or refinances the loan. A row whose trigger has fired and whose feature shipped without paydown gets re-scored at the next weekly review — paid, or escalated with a reason.
Cadence: the weekly review, no new meeting
The register is reviewed inside the meeting you already run — five minutes at the end of the weekly review: re-score interest on any row that caused rework this week, check triggers, archive rows paid since last week with the PR that paid them. Paid rows are not deleted silently; the receipt (the PR link) is the register's credibility.
Five traps
- The graveyard spreadsheet. A hundred-row register with no interest column and no owners, never paid, dying of shame. Ten rows, each with terms, beats a museum of good intentions.
- Debt as shame list. If the register is where shortcuts are used to win arguments, engineers will stop reporting shortcuts — and you will lose the early warning. Reward the report; the debt existed the moment the shortcut shipped, not when it was written down.
- The big-bang refactor quarter. Refactors scheduled away from production pressure drift for months and get cut the first time the quarter gets busy. Pay on trigger, inside the PR that was going to touch the code anyway.
- Size without interest. A backlog of estimates tells you nothing about priority. Interest decides; size only schedules.
- Ownerless rows. A row without a name is an unpaid loan nobody holds. Owner or archive — nothing lives in between.
Worked example
A nine-person fintech SaaS, three-year-old codebase. The feared item: payment retry logic duplicated across three services after a rushed migration two years ago. The old way, this would be "the payments rewrite" — a quarter nobody could schedule. Instead the register carries ten rows; the duplicated retry logic shows interest of roughly six rework hours a month plus two near-miss incidents in two quarters, owner: the payments engineer, trigger: "next feature touching payments." The trigger fired six weeks later. Paydown shipped as two PRs inside the feature work: the new shared retry module with parity tests against both old paths, then deletion of the duplicates a week later. Total added to the feature: nine days. Result: zero payment incidents the following quarter, and the register lost its highest-interest row with a receipt. Two smaller rows (a deprecated retry library, an unmaintained PDF generator) were paid the same way when their triggers fired.
Metrics (for the register itself)
- Ten rows visible or fewer; everything else archived.
- Every row has owner + trigger + interest. A row missing any of the three is deleted, not "fixed later" — a loan without terms is not a loan.
- Interest measured in units the team already tracks (rework hours, deploy minutes, incident count).
- Every paid row cites the PR that paid it; every new row cites the shortcut PR that created it.
- The register is reviewed inside the existing weekly review. If reviewing it needs a new meeting, the register has become the debt.
From the HIVE80lab kit
- The First 30 Minutes — free incident quick-start checklist
- Ops Starter Kit — incident response for small teams — $14
- Ops Starter Kit Vol. 2 — advanced incident response & communications — $27
- Ops Mega Bundle — all 5 kits in one download — $49
Related: the capacity planning template is the same discipline for infrastructure — one row per service, a limit, a trigger; the error budget policy is where debt shows up as spent reliability, and the weekly review checklist is the meeting where the register gets its five minutes.