Home → Leadership
Technical Debt: The Same Queue, Not a Separate Backlog
“We will spend 20% of every sprint on technical debt.” Everyone left that meeting happy. Sprint one: 18%. Sprint two: 12%. Sprint three: 5%. Sprint four: zero. Nobody cancelled the rule — every sprint there was simply “something urgent this time”.
- A percentage is not a budget, it is a wish. Work in a separate bucket is never prioritised, and work that is not prioritised does not get done.
- Measure debt by its interest. “The code is bad” cannot be defended; “the last 12 items in this module took 3 extra days each” can.
- There are three types of debt and two of them are real. Debt that slows you down and debt that creates risk. Merely ugly code is a preference.
- Not paying is also a decision. You do not pay off a module that will be deleted in six months — but you write that decision down.
- The boy scout rule has a limit: PR size. Clean what you touch, but not enough to kill the review.
- A separate list is a debt graveyard. I paid for the same lesson with postmortem actions.
From the field: the death schedule of a rule
In the fourth sprint planning, nobody said “let us drop the 20% rule”. Three urgent items simply arrived and the capacity filled up. The same happened in sprint five. By sprint six nobody remembered the rule existed.
I realised later that the rule was set up to fail from the start. Protecting that 20% required winning a fight every sprint, and the same side kept losing. The urgent item had an owner and a date. The debt had neither.
My mistake was not making the rule, it was making it without a measure. I sat at the table with the sentence “the code is bad, we should fix it”. That sentence always loses to “the customer wants this feature on Thursday”, because one of them has a number and the other does not.
Measuring the interest
The sneaky thing about technical debt is that you pay for it in instalments, not all at once. So what you should measure is not how bad the code is, but how much touching it slows you down.
# 1) From the issue tracker: completed items of the last 6 months
# (estimated duration, actual duration)
# 2) From each item's PRs: which directories it touched
# 3) Match them:
module items est. avg actual avg delta
--------------------------------------------------------------
payments/old-flow 12 3 days 6.1 days +3.1
catalogue 19 2 days 2.3 days +0.3
notifications 8 2 days 2.1 days +0.1
# The resulting sentence:
# "Every item touching payments/old-flow takes 3 extra days.
# Six more items will touch it next quarter: ~18 days.
# The fix takes 5 days."
After this table appeared, the prioritisation meeting changed completely. I was no longer saying “let us make time for quality”; I was saying “let us spend 5 days to save 18”. The second one is not an engineering preference, it is a sequencing argument — and because it is in the same language, it can be discussed.
Sometimes the table says the opposite, as in the catalogue row: the code
is ugly but it slows nobody down. The days spent fixing it would save no days at all.
Three types of debt, two of them real
| Type | Symptom | What to do |
|---|---|---|
| Debt that slows you | Work in that area constantly overruns the estimate | Measure the interest, put it in the same queue. The easiest debt to defend |
| Debt that creates risk | No tests, cannot be rolled back, one person understands it | It may save no days, so defend it with probability, not days |
| Merely ugly code | Uncomfortable to read, no other symptom | Not debt, a preference. Clean it when you touch it; do not open an item |
The second row is the most argued about. Risk debt has no equivalent in days; saying “this service has no tests” slows nobody down today. The way to defend it is a concrete scenario: “We had two incidents in this module last year, and in both it took 40 minutes to find the root cause because the tests told us nothing.” The number still comes from the incident records.
Not counting the third row as debt keeps the list honest. If every aesthetic irritation becomes an item, the list swells and nothing gets done — and the two real types get lost among them.
How it enters the same queue
A debt item is written in exactly the same form as any other item. The difference is the reasoning line:
TITLE : Collapse the duplicate code path in payments/old-flow
SIZE : 5 days
WHY : The last 12 items touching this module took 3.1 extra
days on average. Six more will touch it next quarter.
Estimated saving: ~18 days.
RISK : If untouched, two items slip by the end of the quarter.
HOW : Step by step; each step a separate PR, behaviour
unchanged, tests written first.
OWNER : one name DATE: sprint 21
Three things change: an owner (a person, not a team), a date, and a number. With those three, the item can defend itself in the normal backlog. Without them it falls into a separate list, and that list is a graveyard — I made exactly this mistake with postmortem actions, and the lesson is the same.
The boy scout rule and its limit
Most small debt does not need an item at all: leave the place you touched a little cleaner. But this rule has a limit, and without it you break something else.
- Naming inside the function you touched
- Adding a missing test
- Deleting dead code (if it is really dead)
- Fixing a misleading comment
- Moving or renaming files
- Changing an interface
- Any cleanup that affects behaviour
- Anything that pushes the PR over 400 lines
The reason is in the code review post: a growing PR silences the review.
The real damage of a mixed PR is this: when cleanup and the real change share one diff, the reviewer cannot separate them, and if a rollback is needed, both go back together.
Choosing not to pay
Trying to pay off all debt is as wrong as trying to pay off none. Some debt is carried deliberately:
- Code with a short life. You do not pay off a module that will be deleted in six months.
- Areas nobody touches. If there is no interest, the principal does not matter.
- Boundaries that are not settled yet. Cleaning up before you know the right abstraction just creates a second debt.
One condition: the decision gets written down. If “we are carrying this on purpose, for this reason, and we will revisit it if this changes” never reaches a decision record, the same argument starts from scratch in six months — and this time nobody remembers why it was carried.
What to track
| What | Why |
|---|---|
| Estimate overrun per module | The interest itself; it locates debt better than any code quality tool |
| Items touching that module next quarter | It decides when to pay, not whether |
| Debt items waiting in a separate list | Should be zero; anything else means the list is becoming a graveyard |
| Debt carried deliberately and written down | Growing is good: it means decisions are being made, not postponed |
What did not work for me
- The 20% rule. Described above: dead in four sprints. A percentage is a commitment with no owner and no date.
- A “technical debt sprint”. We set aside a week per quarter. That week everyone cleaned up their favourite annoyance, and none of it was the most expensive debt. Time set aside without a measure goes to whatever irritates most — not to whatever slows you most.
- Making a code quality score the target. The tool produced a score and we aimed to raise it. The score went up and nothing got faster. A score is not a substitute for interest.
Checklist
- Which type is this: slowing, risk, or merely ugly?
- Did I measure the interest — how much longer does work here take?
- How many items will touch that area next quarter?
- Does the item have a single owner and a date?
- Is the item in the normal backlog, or in a separate list?
- Did I also consider not paying it?
- If I decided not to pay, is that written down?
- Is the cleanup mixed into a PR with a real change?
Conclusion
After the 20% rule died we did not replace it with another rule. We made one table instead: how much longer work takes in each module. The next quarter, debt items still came to roughly a fifth of the work — but nobody was talking about percentages any more, because each item had entered the queue on its own merits.
Trying to protect technical debt work is an admission that it cannot be defended. Make it defensible and it no longer needs protection.
The test: can you explain your biggest technical debt to someone on the product side, in days? If not, the problem is not that product does not understand. It is that you have not measured it yet.