A retained compatibility change introduced a contract version and a measured fallback lookup. Related work made acquisition atomic, separated expiration from wall-clock dates, narrowed compatibility fields, enforced authorization, and protected unselected drafts.
That history proves several rules had to become explicit. It does not prove that the old design charged technical-debt interest. The changes might be planned slices, newly discovered requirements, corrections, or work a different design would also need.
“The cart system is technical debt” adds no missing evidence. It does not say which future change becomes harder, what extra work the current design causes, how often that tax recurs, or what bounded repair would remove it.
Technical debt becomes actionable when that tax can be named. Until then, it is usually a judgement about code wearing an accounting metaphor.
Debt belongs to a change path
Old code is not debt merely because it is old. Complicated code is not debt merely because it is unpleasant. A stable component which rarely changes may be cheaper to leave alone than a beautiful replacement.
Debt appears relative to work the product needs to do:
requested change
-> ordinary implementation
-> extra work caused by an existing constraint
-> delivery or operating consequenceThe constraint might be an ambiguous contract, shared mutable state, a slow test environment, an unindexed query, a manual deployment step, or a runtime at its capacity limit. The extra work might be investigation, repeated translation, rework, waiting, incident response, or infrastructure.
This definition avoids grading an entire application. One compatibility path can impose a large tax on existing-cart lookup and none on creating a new cart. The debt entry should follow the affected path.
Commit count is not interest
A run of corrective-looking commit titles is tempting evidence. It is also easy to misuse.
Several changes may be:
- independently planned slices;
- one change copied or combined across branches;
- tests added after behavior was already correct;
- new requirements rather than repairs;
- cleanup discovered while nearby code was open; or
- genuine rework caused by a weak contract.
The retained compatibility history contains related and overlapping changes. Counting every diff as a separate failure would exaggerate the work. Counting only the final combined diff would hide the decisions made along the way.
The honest initial row looks like this:
| Field | Retained evidence |
|---|---|
| change path | existing-cart compatibility |
| observed work | contract version, fallback lookup, acquisition, expiration, authorization, draft protection |
| active effort | not retained |
| elapsed delay | not retained |
| repeated tax | not established |
| consequence | not retained |
| cause confidence | several contracts changed; avoidable debt is unproven |
This row does not justify a rewrite. It identifies what must be measured the next time the path changes.
Elapsed delay and engineering effort must also stay separate. A long calendar delay is not automatically the same number of engineer-days.
It may contain:
- waiting for a test environment;
- discovering which environment-specific branch ran;
- coordinating a release;
- repairing incompatible data;
- repeating manual verification;
- waiting for an external answer; and
- active implementation.
Those are different taxes with different repairs. More engineers will not fix environment waiting. A contract test will not remove release coordination. Automation will not clarify a product rule which nobody owns.
Record those parts separately rather than converting calendar time into fictional salary cost or a return on a six-month rewrite.
Interest has more than one unit
The debt metaphor becomes misleading when every consequence is converted into one score.
Useful units include:
delivery
elapsed lead time
active engineering hours
blocked changes
quality
corrective changes
escaped defects
repeated investigations
operations
incident minutes
queue age
saturation
manual release steps
optionality
unsupported customer cases
changes rejected because the path cannot carry themDo not add incident minutes to query latency and call the total 83 debt points. Keep each unit visible so the proposed repair can be matched to the
cost it is supposed to remove.
The retained compatibility work illustrates the limit. Its history proves change, not how much an earlier design cost, whether each follow-up was avoidable, or whether they share one cause. Commit count is not an invoice for technical debt.
An interest ledger needs a counterfactual it can defend
An actionable entry can fit in one row:
| Field | Question |
|---|---|
| path | Which requested work crosses the constraint? |
| observed tax | What extra delay, effort, failure, or capacity did we see? |
| comparison | What makes the work “extra” rather than ordinary complexity? |
| recurrence | How often has this class of work appeared? |
| confidence | Which part is measured, recalled, or inferred? |
| bounded repair | What is the smallest intervention expected to remove the tax? |
| proof | Which next observation would show that the tax changed? |
| expiry | When should the entry be reviewed or discarded? |
The comparison is often the weakest field.
A similar change which does not cross the boundary is useful. So are before-and-after traces, repeated phases within one delivery, or a controlled reproduction. “This would be easy in a clean system” is not a counterfactual. It is a preference.
The retained compatibility case has no defensible comparison yet. It shows
several rules becoming explicit, not how long an alternative design would
have taken. Its debt entry should remain measure until another occurrence
supplies that comparison.
The repair must name the tax it removes
“Modernize the legacy application” is not a debt payment. It is a programme whose relationship to the observed tax remains unproven.
A bounded repair might be:
- record the shared request and response contract in executable examples;
- make test and production execute the same business rule;
- provide deterministic fixtures for the affected data shapes;
- replace a manual release step with an enforced gate;
- isolate one state owner; or
- route one recurring change class through a stable adapter.
Each proposal should complete the sentence:
If we do this, the next change on this path will no longer need this specific extra work.
Then define the check. The next three cross-boundary changes might be expected to complete integration within an agreed elapsed range, require no manual data repair, or pass the same contract suite before release. The observation does not need to promise perfection. It needs to be capable of proving the repair irrelevant or insufficient.
If the repair cannot name a future observation, it may still be worthwhile design work. It is not yet an evidence-backed debt payment.
Paying, containing, and accepting are all valid
An identified tax does not automatically deserve removal.
There are four ordinary choices:
| Choice | When it fits |
|---|---|
| accept | the path is rare and the bounded repair costs more than likely recurrence |
| contain | a wrapper, checklist, specialist review, or capacity limit makes the tax affordable |
| pay down | recurring measured cost is likely to exceed a scoped repair |
| replace | the constraint is structural and narrower repairs cannot meet required behavior or capacity |
Containment is often underrated. A stable adapter and focused reviewer may turn a dangerous integration into an acceptable one without making the legacy system elegant. Acceptance can also be rational when a component is near retirement or the company has more valuable work.
Risk can override the time comparison. A rare path which can duplicate payments, expose another customer’s data, or corrupt durable state may need containment or removal before its expected engineering hours exceed the repair. Those consequences should remain named risks rather than being disguised as a larger time estimate.
The synthetic reproduction uses ranges rather than a universal debt score. It recommends a repair only when the lowest projected recurring effort exceeds the highest repair estimate, accepts only when the ranges are separated in the other direction, and asks for more evidence when they overlap. An unacceptable risk is contained regardless of the hour comparison.
That arithmetic is a decision aid, not an economics model. Frequency can change, estimates can be wrong, and removing one bottleneck can reveal another. Its value is that every input and assumption remains visible.
A debt register should be allowed to forget
Debt backlogs tend to become permanent museums of engineering frustration. Entries survive after the feature is removed, the team changes, or the constraint stops affecting planned work.
Every entry needs an expiry condition:
- a date;
- the next occurrence;
- completion of a named product plan;
- retirement of the affected path; or
- evidence that the tax no longer recurs.
Reviewing may close the entry without changing code. That is not failure. If the product no longer needs the path, the debt stopped charging interest.
Technical debt is most useful as a claim about future work: this existing constraint makes this needed change slower, riskier, or more expensive in this observable way. Name the path, keep the units honest, propose a bounded repair, and check the next occurrence. Anything broader may be a design opinion worth discussing, but it is not yet a debt account the business can act on.