River
Y CombinatorBacked by Y Combinator
FREE TEMPLATE

Technical Debt Tracking Register

Four documents and four sheets, including the register column that says what each item is costing you per year in engineering hours.

Free download  ·  No account needed

Every debt register in circulation has the same columns: an ID, a description, a severity from one to five, an estimated effort, an owner, a status. It loses to the roadmap every quarter, and not because the engineers explained it badly. Severity and delivery weeks are not comparable quantities, so there is no honest way to put the register beside the feature list and choose. The column that would make them comparable is the one nobody fills in.

It has been named since 1992. In Ward Cunningham's original report on the debt metaphor, the definition is one sentence: every minute spent on not-quite-right code counts as interest on that debt. Not the cost of the rewrite. The tax on ordinary work while the rewrite has not happened. That is measurable from records you already keep, as the excess hours a module costs per change over the codebase median, times how often it actually changes.

Sedgemoor, the billing platform in the worked example, had 47 items and a severity column. Measured, 16 of them sat in code with no commit in twelve months, two of the team's own top four among them. Four of the ten most expensive modules in the codebase were not in the register at all. Invoice rendering, ranked seventh, cost 614 hours a year, more than the top three combined, because it changes 118 times a year and each change runs 5.2 hours over the median.

The register, re-ranked by what each item actually costs

Three sheets from one codebase. What each item costs per year, what it costs when a change goes wrong, and what fixing it returns.

Debt Register

Illustrative rows for a fictional B2B billing platform, Sedgemoor. Interest is the excess hours per change over the codebase median of 3.1, times the change count in twelve months.

ModuleSeverity rankChanges, 12mHours per changeInterest, h/yrRank by interest
Invoice rendering71188.36141
Tax rules engine2629.64032
Subscription state machine15210.23693
Webhook dispatchnot in the register946.93574
Legacy pricing tables3312.62811
Currency conversion, old5111.4813
Data import v140not measurable0none

The severity column and the measured column disagree almost everywhere. Invoice rendering was ranked seventh and costs more per year than the top three combined, because it changes 118 times to their 52 and 62 and 3. Nothing about it felt urgent. Every single change to it was merely tedious.

Two of the team’s own top four are in code nobody touches. Legacy pricing tables had 3 changes in a year and data import v1 had none at all, so its interest is zero however bad the code is. Across the full 47-item register, 16 items sat in modules with no commit in twelve months.

Four of the ten most expensive modules in the codebase, carrying 780 hours a year between them, were not in the register at all. The largest was webhook dispatch, where every new event type needed edits in three places and nothing failed at compile time when one was missed.

Change Failure Correlation

The join against twelve months of incidents and reverted changes. This is the half of the argument a product manager already believes.

ModuleChangesIncidentsRevertedFailure rateAgainst baseline
Invoice rendering11831714.4%4.5x
Tax rules engine620812.9%4.0x
Webhook dispatch9411111.7%3.7x
Subscription state machine524611.5%3.6x
Auth middleware77145.2%1.6x
Ledger posting31000.0%unevidenced
ALL FLAGGED MODULES6199 items14 items11.8%3.7x
ALL UNFLAGGED MODULES1,104nonenone3.2%1.0x

The last two rows are the whole meeting. A change touching at least one flagged module was reverted or rolled back 11.8 percent of the time. A change touching nothing flagged, 3.2 percent. One ratio, 3.7 to one, computed from the team’s own merge history rather than from an industry report.

Thirty of the 47 items had neither an incident nor a failed change over a full year. They are relabelled unevidenced and left in the register rather than deleted. Deleting an entry starts an argument about the register. Relabelling one starts an argument about the evidence, and that argument has an answer.

Reverted changes are usually easier to extract than incidents, because they live entirely in version control. A revert, a rollback, or a fix change landing inside forty-eight hours and referencing the same work all count.

Remediation Plan

Interest is gross. The recovery fraction is the share of it the engineer giving the estimate will commit to removing, and it is the column that reorders this sheet.

ModuleFix hoursInterestRecoveryReturns, h/yrPayback
Webhook dispatch12035760%2146.7 months
Invoice rendering24061455%3388.5 months
Payment provider adapter8017845%8012.0 months
Subscription state machine20036950%18413.0 months
Tax rules engine16040335%14113.6 months
Legacy pricing tables1202870%2072.0 months
Data import v1320080%0never
FUNDED SET, four items7201,743blended8779.9 months

The tax rules engine carries the second-highest interest and lands fifth. The engineer who would do the work would only commit to removing about a third of the excess, because the rest is the tax code and no refactor removes that. Nobody asks for this number, and it changes the order.

Data import v1 has no case at any fraction. Eighty percent of zero is zero. It had been ranked fourth by severity two years running, and the arithmetic retires the argument in one line rather than in another meeting.

The four funded items took 720 engineering hours and return 877 hours a year, a blended payback of 9.9 months. That is a proposal a product manager can compare against a feature, because both are denominated in the same unit.

What's in the pack

01

Module Change Profile

Every module in the codebase with its change count, median hours per change, contributor churn and last commit date, whether or not the register mentions it.

02

Debt Register

Each item carrying a measured interest figure in hours per year, its old severity rank and its new rank, so the disagreement between the two stays visible.

03

Change Failure Correlation

The join against incidents and reverted changes, ending in one ratio: the change failure rate for code that touches a flagged module against code that does not.

04

Remediation Plan

The only sheet allowed to hold an estimate, with a recovery fraction beside each one, because a refactor rarely removes all of a module's excess cost.

05

How Interest Is Measured

The method, written so somebody who was not in the room can reproduce every figure, including what the measurement cannot see and why the incident join sits beside it.

06

Debt Assessment

The standing position: total interest as a share of team capacity, split by temperature, by evidence and by the mechanism that makes each change expensive.

07

Prioritisation Rationale

The document that goes into planning, opening with four numbers and containing a section naming the high-severity items that lost their rank once measured.

How to use it

  1. 1

    Open in River, or take it blank

    Send the pack twelve months of change history inside River, or download the four documents and four sheets and work through the method without an account.

  2. 2

    Build the change profile first

    One baseline figure for the whole codebase, then a change count and a cost per change for every module. Every interest figure is an excess over that baseline.

  3. 3

    Rank everything, not just the register

    Sort all modules by measured interest and compare against what somebody wrote down. The gap is usually the most useful output of the first pass.

  4. 4

    Join, then cost, then ask

    Attach incidents and reverted changes to each item, set a recovery fraction with the engineer who would do the work, and compute payback in months.

Frequently asked questions

Is this template free?

Yes, and the download needs no account and no card. Edit with AI is the optional half: it reads the change history and incident records you send, builds the module profile, and measures interest on every item. Every other pack sits in the template library.

What format are the downloaded files?

Four documents as Word files and four sheets as CSVs, in one zip. The Module Change Profile and Debt Register arrive with their columns ordered and the Sedgemoor rows in place as worked examples, so the arithmetic is visible before you replace them with your own.

We do not track engineering hours per change. Can we still use it?

Yes. Use elapsed time from first commit to merge on the pull requests touching each module, and label the column so nobody compares the two units. A consistent proxy everybody can see beats a scored guess. The ranking holds because every module is measured the same way.

Our static analysis tool already reports a technical debt figure.

That is a different quantity with a similar name. SonarQube's own definition sums a remediation cost attached to each rule that fired, over a cost to develop one line of code, thirty minutes by default, times the line count. Both terms are constants.

So why does that figure never move anybody?

Because neither term looks at the change history, it cannot fall when a module goes cold. At Sedgemoor, 41 percent of a reported 358 days sat in modules with no commit in twelve months. An accurate estimate of a cleanup nobody needed.

What about debt that is a risk rather than a cost?

An interest figure cannot see a danger that has not yet cost anything, and a rarely changed module handling money will sit near the bottom. That is why the incident join sits beside the interest column rather than replacing it. Make the risk argument separately, on its own terms.

What else belongs alongside this?

The measurements either side of it. Engineering metrics and delivery review covers throughput and change failure across the team, inherited codebase documentation maps a system nobody has context on, and postmortem action follow-through tracks what incidents were supposed to fix. The contributor guide is worth sending to whoever is about to touch one of these modules for the first time.

Put a number on the register that a product manager can argue with

Take the Word documents and CSV sheets blank, or open this exact pack in River and send it twelve months of change history.

Edit with AI