DebtGENERALSCALE-SPECIFICCONTESTED

The Debt Register

Five fields per entry — Problem, Impact, Risk, Owner, Trigger — and a hard rule about size. A register with two hundred entries is not a register, it is a wishlist nobody reads.

The requirement, the obvious build, and why it breaks

Every lesson starts where the work starts: someone asked for something, and the first implementation that comes to mind survives until the requirement changes.

The question

How do we keep track of the debt we are carrying without producing a list that grows forever and gets read never?

The requirement

After a bad quarter, the team agrees to "start tracking technical debt". Six months later there are 214 open items, none has been closed, and the document has not been opened since March.

The obvious build

Make a list. Anyone can add to it, we review it quarterly, and we allocate twenty percent of each sprint to working through it.

Why it breaks

An unbounded inbox with no exit criteria fills up. Adding is free and closing requires work, so the list grows monotonically and its median entry gets older every month (The Utility Dumping Ground has the same dynamic in code).

How it breaks as requirements change
  • An unbounded inbox with no exit criteria fills up. Adding is free and closing requires work, so the list grows monotonically and its median entry gets older every month (The Utility Dumping Ground has the same dynamic in code).
  • A list of 214 items cannot be prioritised, so the twenty percent gets spent on whatever is nearest, which is uncorrelated with what is expensive (Interest: Why Debt Compounds).
  • Quarterly review is the wrong trigger. The moment an entry matters is when someone is about to work in that area, which is a per-change event and not a calendar one (Revisit Triggers).
  • Because entries have no owner and no impact, the review becomes a reading exercise, gets cancelled twice, and the document dies — which is worse than never starting, because now the team believes tracking debt does not work.
RequirementConstraintsInvariantsResponsibilitiesBoundariesInterfacesStateDependenciesFailureImplementationTestsFeedbackEvolution

What limits the solution, and what must never stop being true

This domain leads with these two. A design that ignores its constraints is not a design, and an invariant nobody named is one nothing is protecting.

Constraints
  • Any process that costs more than ten minutes per entry will not be followed under deadline pressure.
  • The register has to be visible to the people who prioritise work, which means it lives where work lives, not in a wiki.
  • The team is five engineers; there is no capacity for a debt-management role.
Invariants
  • Every entry names a future change it makes more expensive. An entry that cannot is not debt and does not belong here (What Technical Debt Actually Is).
  • Every entry has exactly one named owner, and an entry whose owner has left is either reassigned or dropped.

Who owns what, and where the seams fall

Responsibilities decide boundaries; boundaries decide what an interface has to say.

Responsibilities
  • Each entry has one named owner who is responsible for its accuracy and for closing or dropping it — not for repaying it personally.
  • Whoever takes deliberate debt writes the entry the same day, because the reasoning is only available then (Deliberate Debt).
  • The team owns the size limit, and enforcing it means dropping entries, which is the activity everyone avoids and the one that keeps the register alive.
Boundaries
  • The register lives where work is prioritised — the backlog, the issue tracker — not in a document, because a debt item that cannot compete with a feature for attention will never be scheduled (Decision Records).
  • The boundary between a register entry and a decision record is the trigger: an ADR says why we chose this; a register entry says what we are carrying and what would make us stop (Architecture Decision Records).
  • Entries are scoped to an area. "The codebase is inconsistent" has no boundary and therefore no owner and no trigger.

Five fields, and what each is for

The format is short because length is what kills registers. Every field earns its place by being something a future reader cannot reconstruct: what the choice was, what it costs, whether it is contained, who to ask, and what would change the decision.

The Impact field is the one that decides whether the register works. Written as edits or as a named risk, entries can be compared. Written as an adjective, they cannot, and prioritisation reverts to who argues hardest.

  • Problem — the choice, not the feeling. "The rule lives in four modules", not "the code is messy".
  • Impact — what the next change costs, in edits, modules or named risk (Change Amplification).
  • Risk — what could go wrong while you carry it, and whether it is contained (Deliberate Debt).
  • Owner — a person, not a team. A team-owned entry is an unowned entry (Code Ownership).
  • Trigger — an observable event, not a date and not a mood (Revisit Triggers).
One entry, and the same entry written badly
1GOOD
2Problem Pricing rule is implemented in 4 modules; no owner.
3Impact Every pricing change: 4 edits, 6 comparisons, 2 teams.
4 Last 3 changes took 3d, 4d, 6d.
5Risk High. The 4 copies already disagree on rounding for
6 refunds. No test compares them.
7Owner M. Salah
8Trigger Next pricing change, OR a 5th copy is proposed.
9
10BAD
11Problem Pricing code is messy and hard to maintain.
12Impact Slows us down.
13Risk Unknown.
14Owner Backend team
15Trigger When we have time.

The two describe the same code. Only the first can be compared against a feature, argued about with evidence, or closed — and only the first tells a future reader that the copies already disagree, which is the thing they most need to know before touching it (Interest: Why Debt Compounds).

The lifecycle of an entry

Entries have states, and most dead registers are dead because two transitions are missing: nothing is ever dropped, and nothing is ever closed with a verification. Making the lifecycle explicit is what turns a list into something with a throughput you can look at.

The forbidden transitions are the interesting part. Each of them is a shortcut that a busy team will take, and each of them quietly converts the register back into a wishlist.

A register entry
ProposedCarriedTriggeredRepayingRepaid ·Dropped ·
FromOnToGuardEffect
ProposedReviewCarriedImpact is stated as edits or named risk, and an owner accepted itCounts against the register cap, which forces a comparison with what is already there
ProposedReviewDroppedNo future change is made more expensiveRecorded with the reason, so the same item is not refiled next quarter
CarriedThe trigger fires, or work opens in the areaTriggeredSurfaces to whoever is about to work there
TriggeredDecision to repayRepayingBounded, and tests exist that would catch a behaviour change
TriggeredDecision to keep carryingCarriedA new, different trigger is writtenA deliberate re-decision rather than a drift (Revisit Triggers)
TriggeredThe area is being deleted or replacedDropped
RepayingThe next real change in the areaRepaidThe claimed saving was actually observed
CarriedCap enforcementDroppedLowest impact in the register, and the trigger has not fired in a year
must be impossible
  • Proposed → CarriedWithout a stated Impact and a named owner. This is the single transition that produces 214-item registers: accepting entries costs nothing, so everything is accepted and nothing can be compared.
  • Carried → RepaidSkipping the trigger and the verification. An entry closed because someone restructured the area, with no check that the claimed saving appeared, teaches the team nothing about whether its debt judgements are any good.
  • Repaid → CarriedReopening a closed entry instead of filing a new one. The old entry's Impact described a design that no longer exists, so reopening it smuggles a stale argument into a new decision (Decision Records).
  • Dropped → RepayingWorking on something the team decided not to carry. If it deserves work it deserves a new entry with a current Impact — otherwise the register is not where prioritisation happens and the whole apparatus is decorative.

The two transitions teams skip are Carried to Dropped and Repaying to Repaid. Without the first the register grows without bound; without the second nobody ever finds out whether the debt judgements were correct.

How registers die

Every failure below has been observed in real teams and each one looks like diligence at the time. The response column is deliberately cheap, because a heavyweight remedy is itself a way for the practice to die.

Diagnosing a register that is not working
TriggerSymptomCauseResponse
Nothing has closed in a quarterThe list only grows; the oldest entry is the oldest entry every monthNo cap and no drop discipline, so accepting is freeSet a cap and enforce it by dropping, not by refusing to add (The Complexity Budget).
Entries have no owner or a team as ownerEvery entry is discussed and none is acted onDiffusion of responsibility, which a team name formalisesOne name per entry; reassign on offboarding (Code Ownership).
Impact fields are adjectivesPrioritisation by advocacy; the loudest engineer's items get done"Hard to maintain" cannot be compared with anythingRewrite Impact as edits, modules or a named risk. Drop entries whose author cannot (Change Amplification).
Static analysis findings imported wholesaleHundreds of entries appear overnightFindings are not entries — no owner, no trigger, no future change namedKeep the tool's output in the tool. Promote an item to the register only when someone will own it (What to Automate Out of Review).
The register is a wiki pageIt is not opened between reviewsIt cannot compete with features because it is not where work is prioritisedMove it into the backlog with a label, so entries surface next to the work they affect.
Entries are written about other teams' codeThe register reads as a list of grievancesIt has become a place to file design disagreements rather than have themAn entry requires the owning team to accept it. If they will not, that is a design conversation, not a register item (Tone, Disagreement and Receiving Review).

How to build it

Most important first.

  • Five fields, no more: Problem (what the choice was), Impact (what it makes expensive, in edits or in risk), Risk (what could go wrong while carrying it, and how it is contained), Owner (a name), Trigger (the observable event that reopens the decision).
  • Cap the register. Twenty entries for a team of five is generous; the cap forces a comparison every time something is added, which is the whole mechanism (The Complexity Budget).
  • Attach entries to areas of the code, so that starting work in an area surfaces its entries automatically rather than depending on anyone remembering.
  • Review by exception, not by calendar: an entry surfaces when its trigger fires or when someone opens work in its area.
  • Drop aggressively. An entry whose trigger has not fired in a year is evidence that the change never arrives, which means it was not debt (When Design Does Not Pay).

What the next change costs

The field this whole domain exists for. A structure is only better if it makes the change after this one cheaper — and it is worth saying which changes it does not help.

Cost of the next change
  • Without a register: the next change in a debt-laden area costs the surcharge plus a rediscovery of why the code is like that, every time, by whoever happens to be there (Accidental Debt).
  • With a 214-item list: the same, plus the cost of maintaining a document nobody reads, plus the credibility spent on a debt initiative that visibly produced nothing.
  • With a capped, owned register: the next change in the area starts with a five-line briefing on what is being carried there and what would justify fixing it, which turns a rediscovery into a read.
  • The compounding effect: because entries surface when work enters their area, repayment happens opportunistically, which is the only mode in which it is reliably affordable (The Refactoring Loop).
What the recommended approach costs
  • A cap means real problems get dropped, and some of them will come back and cost money. That is the price of a list that gets read.
  • Writing entries is a tax on the moment when a team is most under pressure, and it will sometimes be skipped exactly when it is most valuable.
  • Making debt visible to the business invites the question of why it exists, and answering that honestly costs political capital that some teams cannot afford to spend (Design Review).

What can go wrong

Failure modes
  • It becomes a graveyard: entries accumulate, nothing closes, and its existence is cited as evidence that the team knows about its problems while nothing is done about any of them.
  • It becomes a weapon: entries are written about other people's code as a way of registering disapproval, and the register turns into a place where design arguments are filed rather than had (Tone, Disagreement and Receiving Review).
  • Impact is written as an adjective — "makes the code harder to maintain" — so entries cannot be compared and prioritisation falls back to advocacy.
  • The mitigation fails: a size cap forces dropping entries, and the entry that gets dropped is the one with the least articulate advocate rather than the least expensive problem. That bias is real and the only defence is that the Impact field is written in edits rather than adjectives.
Dependencies, and their direction
  • The register depends on the issue tracker the team already uses; a bespoke tool is a dependency with a maintenance cost and no adoption (Build, Library, SaaS or Managed Service).
  • Triggers depend on being observable somewhere the team already looks — a backlog label, a code-owner file, an alert — or they are decorative.
Misreads
  • "The register should be complete." Completeness is the failure mode. It is a prioritised carry list, not an inventory of everything imperfect (What Technical Debt Actually Is).
  • "Twenty percent of every sprint" fixes it. A fixed allocation without prioritisation spends the budget on whatever is nearest to hand. The allocation is not the problem; the ordering is.
  • "A tool will fix it." Static analysis produces findings, not entries: no owner, no trigger, no impact in terms of a future change. Importing them wholesale is how registers reach 214 items (What to Automate Out of Review).
  • "Every entry needs an estimate." Estimating repayment for items you may never repay is wasted work. Estimate when the trigger fires, which is when the number is needed and when it is most accurate.
Smells this explains
  • utility-dumping-ground
  • duplicate-knowledge

Testing it, and how it ages

What to test, and at which boundary
  • The register is testable in one way that matters: pick three entries at random and ask whether anyone can state the change each makes expensive. If not, the Impact fields are adjectives.
  • Check the close rate. A register where nothing has closed or been dropped in a quarter is not being used, whatever its size (Revisit Triggers).
  • Before closing an entry, verify the claimed saving in the next real change in that area; if nobody can point at it, record that the entry was mislabelled.
How this design ages
  • A healthy register oscillates around its cap and its average entry gets more expensive over time, because the cheap items are repaid opportunistically and the structural ones remain.
  • An unhealthy one grows monotonically, and the tell appears within about two months — long before anyone declares it a failure.
  • Ownership decays through turnover faster than anything else in the register, so reassignment has to be part of offboarding or the entries silently become orphans (Bus Factor).

Where this applies

This domain's advice is contested more than most. These labels say what each claim is specific to — and where CONTESTED appears, the note gives the strongest form of the opposing view rather than a caricature.

  • GENERALProblem, Impact, Risk, Owner and Trigger are not technology-specific; what changes across teams is where the register lives, since a team using a lightweight tracker and one using a formal change process will attach it to different artefacts.
  • SCALE-SPECIFICAt five engineers the register is a shared list and the cap can be a convention. At two hundred, entries need an owning team rather than a person and the cap has to be per-area, because a global cap makes teams compete for slots in a list they do not share.
  • CONTESTEDThe strongest opposing view is that debt registers are theatre: the entries that matter are known to everyone on the team without a document, the ones that are not known are not important enough to act on, and the register's real function is to let people feel that filing an entry is equivalent to fixing something. Teams that have watched a register rot find this compelling, and the honest concession is that a register with no close rate is exactly the failure they describe — the cap and the drop discipline exist to make it falsifiable.

Where the depth lives

This domain teaches the codebase-level structure and hands the rest off.

Architecturemodular-monolith
Domains that do not exist yet
  • System Design — the same five-field discipline applies to carried operational risk, where the Trigger is usually a threshold on a metric rather than an event in the backlog.