Skip to content
Delivery practice

Reducing Technical Debt Without Stopping Feature Work

By Sebastiaan Jansen · · 9 min read

Reducing technical debt means paying a planned share of capacity every cycle for known code, test, and design problems, while feature work continues on the rest. Rank debt by the cost it adds to current delivery. How old or ugly it looks doesn't matter. Teams that treat paydown as optional lose that slot the moment a feature slips.

What reducing technical debt means in practice

Ward Cunningham's metaphor still holds: shipping a design you know is incomplete is like borrowing. You get the feature sooner, and you pay interest every time the next change has to route around that shortcut. The original write-up of the metaphor separates that deliberate trade from mess you did not choose.

Debt worth scheduling falls into four piles:

  • Deliberate: you shipped a narrower design to hit a date, and you wrote down what you skipped.
  • Accidental: a reasonable design aged badly because the product moved.
  • Bit rot: dependencies, flaky tests, and copy-paste that nobody owns.
  • Missing knowledge: one person understood a module, they left, and the tests do not explain it.

Interest is concrete. A one-line pricing change takes two days because three services duplicate the rule. A release waits on a manual checklist. A new hire needs a week to get set up because the doc is from last year. If you cannot name the interest, the item is a preference, and it waits.

Tech debt management means that interest sits in the same backlog as features, with an owner and a cost, so it competes in planning instead of living in a side document.

Why a feature freeze fails

Technical debt in agile is easier to carry as a standing part of the sprint, the way bugfix and support work already are. A freeze is a one-off project that shrinks as soon as an incident lands, and an unranked list means people fix whatever annoys them. A capacity slice is a policy, and a policy survives a bad quarter.

A freeze makes sense only when two things are true. A single module blocks the roadmap, and a short rewrite is cheaper than routing around it. And you can name the exit: tests green, the old path deleted, the next features unblocked. "Make it clean" is not an exit. Otherwise, keep shipping and fund paydown from a reserved slice.

A weekly system for reducing technical debt

Run this in planning. Once the backlog exists, it takes about half an hour.

Keep one debt backlog

Each item needs four fields:

Field What to write
Interest The delay, incident, or rework this causes now
Scope The module or pipeline, and what "paid" means
Size Under a day, two to three days, or a week-plus
Link The feature, incident, or requirement that exposed it

"Refactor the billing package" is not an item. "Extract the shared price rule so the discount story is one change instead of three" is.

Link the item to a requirement when you can. A requirements traceability matrix shows which behaviors the cleanup must still satisfy. The product requirements document for the next feature should name the debt it depends on, next to the user-facing scope.

Reserve a fixed slice

Pick a percentage and don't renegotiate it mid-sprint. Fifteen to twenty percent is a common starting band, though that's a policy choice and not a benchmark. Support-heavy teams often need more. A launch month can drop to 10 percent if the missed amount is booked into the next two sprints.

Measure it in the same unit as features: people-days, points, or ticket count. If you plan 10 people-days and reserve 2, those 2 days go to named debt tickets before features fill the rest.

Don't hide the slice inside inflated estimates. "This story is 5, but really 3 plus cleanup" hides the debt and muddies velocity. Put the debt in its own ticket in the same sprint.

Rank by interest, then by the next feature

  1. Debt that can cause an incident or a missed commitment this cycle.
  2. Debt on the path of a feature already planned for the next two cycles.
  3. Steady weekly interest: slow builds, flaky tests, repeated manual steps.
  4. Everything else.

Item 4 waits. Reducing technical debt is mostly a sequencing problem, and the loudest complaint often spends the slice on code nobody is about to change.

Refuse new debt in the definition of done

The cheapest payment is on debt you never add. Put the rules in the definition of done:

  • New behavior has a test where it can fail.
  • A shortcut is removed in the same ticket, or filed with an interest line before merge.
  • A required dependency bump lands in the same pull request, or has a date.
  • The ticket links to the requirement it implements.

A definition of ready closes the other leak. "Clean this up while you are there," with no scope, is not ready. It grows until the feature slips, and then the cleanup is what gets cut.

Check the balance once a sprint

Ask three questions:

  • Did the slice land on the tickets you named?
  • What new debt was filed, and who owns the interest line?
  • Did cycle time on the area you touched move as expected?

Skip the single debt score. If the pricing change still takes two days after you called the ticket done, the scope was wrong.

A worked example

This is an illustration, not a case study. Six people work on a B2B billing product in two-week sprints, with about 45 people-days left after meetings and support. They reserve 20 percent, which is 9 days.

The next feature is percentage discounts. The price rule is copied in the API, the invoice PDF, and a nightly reconciliation job. Last quarter a mismatch between them caused a support incident and a manual credit, and every pricing change still takes three edits.

They split the work like this:

  • Debt, 5 days: one price module, three call sites switched, tests pinned to current outputs, old copies deleted.
  • Feature, 6 days: percentage discounts on that module, including the invoice line and the job.
  • Debt, 2 days: job fixtures, which are why tests skip the discount path.
  • The other days: committed features and support, left alone.

The discount ticket does not start until the price module is in review. The product specification lists "single price module" as a dependency, so the split is visible outside the planning meeting.

In review they check one fact: a pricing change is now one diff. The fixture ticket slipped, so it goes first in the next slice, and nobody pretends the 20 percent was met. What carries over to other teams is the pattern: name the interest, size it, put it in front of the feature that needs it, and keep the rest of the roadmap moving.

Where the slice gets lost

It becomes a junk drawer. Bugs and "someone should look at logs" end up in the 20 percent. Production bugs belong on the incident path. The slice is for named debt with an interest line.

Cleanup turns into a rewrite. A week-plus item needs an end state: what stays, what goes, and how you will know. If you cannot say that in a few lines, split it. A rewrite that eats several sprints is a roadmap bet and gets counted next to features.

Nothing checks the payment. A "refactor" with no deleted code and no faster test is just a status change. Done means the interest moved: fewer edit sites, the job running in CI, a setup step gone.

The roadmap assumes full feature capacity. Plan the quarter at 100 percent features and debt is the first thing cut. Show 80 percent features and label the rest. A roadmap counted from tickets, debt included, is worth more than a slide about quality.

Friday heroics. Unreviewed rewrites add new debt that costs more than the old. Paydown goes through the same review and release path as features, and the old path gets deleted only after production runs the new one.

Use the meetings you already have. Refinement writes the interest line and splits large items. Planning fills the slice before features. A debt ticket blocks like any other, so pulling someone off it means naming the day you gave up. Review reports the interest cut next to the feature.

The Scrum Guide treats the sprint backlog as the increment plus the work required to deliver it. Debt on the critical path is that work. Moving it to a side track is what makes it optional.

What to measure

Watch the interest rather than the team's mood.

  • Cycle time on the module you paid down, before and after.
  • Rework in that area inside the same sprint.
  • Age of the top five. An item that sits at the top for three sprints is too big or not urgent, so split it or rerank it.
  • Slice kept: planned debt work versus shipped. Twenty percent planned and 5 percent finished is a planning failure.

"We closed 40 debt tickets" means nothing if they were comment edits. "The discount story touched one module" is a fact the roadmap can use.

What the tracker will not do for you

Any board with types, stages, and a backlog you plan from is enough. A second list in a doc fails, because debt that is not a ticket never gets a day.

The loop you want has three parts: triage, so a shortcut filed at merge doesn't sit unlabeled; a cycle that rolls unfinished paydown forward; and a roadmap counted from those tickets. Falrow does that. Slack threads, call notes, and production errors become tickets, and debt and features share one capacity count. The mechanics that matter are tickets, triage, and workflow rules, and the delivery loop from request to shipped work.

Jira, Linear, or a well-kept GitHub board can hold the same policy. No tool reserves the slice for you.

Slow feature starts when repairs dominate one area, or when two roadmap items share an unpaid module. Frame it as a product decision with an end date: "The second discount variant waits until price lives in one module."

Reducing technical debt while features continue is something you can audit every sprint. You need reserved capacity, an interest line on every debt ticket, a definition of done that files new shortcuts in the open, and paydown sequenced ahead of the feature that needs it.

FAQ

How much time should a team spend reducing technical debt?

Reserve 15 to 20 percent of sprint capacity and hold it for a few sprints before you change it. Raise it when support and rework already fill the week. Drop it only for a named launch, and book the missed days into the next sprints. The right number is the one you actually finish as separate tickets.

Can we reduce technical debt without a feature freeze?

Yes, unless one module blocks the roadmap and a short, named rewrite is the cheaper path. Rank debt by the cost it adds to work you are about to do, reserve a slice, and pay that debt just before the feature that depends on it. The rest of capacity stays on features and support.

What does technical debt mean on an agile team?

Technical debt in agile is unfinished engineering work that makes the next backlog item slower or riskier, and it lives in the same sprint backlog as features. That covers deliberate shortcuts, designs that aged out, bit rot, and missing tests. Plan it in refinement, finish it under the same definition of done, and judge it by whether the interest fell.

How do we stop new debt while paying the old debt down?

Put the rule in the definition of done: tests for the new behavior, and every shortcut either removed or filed with an interest line before merge. Use a definition of ready so "clean this up as well" cannot enter a feature ticket without a scope. Each sprint, list the new debt that appeared, so the slice is not only paying for last year.

More on delivery practice