Every software product carries technical debt: shortcuts taken to ship sooner, designs that made sense at the time, libraries that have aged. Some of it is a sensible trade-off. The problem is debt nobody tracks, which quietly slows every release.
Recognise the symptoms
- Simple changes take days because they touch many parts of the system.
- The same kind of bug keeps coming back in the same area.
- Only one or two people dare to change certain modules.
- New developers take months to become productive.
- Deployments are rare, manual and stressful.
Make the debt visible
You cannot prioritise what you cannot see. Keep a simple register alongside your backlog. For each item, note:
- Where it is and what the problem is.
- The cost of living with it: slower changes, incidents or risk.
- A rough effort to fix it.
Engineers usually know where the debt is. Ask them, and treat their answers as product information rather than complaints.
Decide what to pay down
Prioritise debt in the parts of the system you change most often. A messy module nobody touches can wait; a messy module that every feature passes through is costing you every sprint.
A useful test: if fixing it would make the next three planned features noticeably faster or safer, do it now.
Pay it down without stopping delivery
- Reserve capacity. Many teams allocate 15–20 per cent of each sprint to improvement work.
- Follow the scout rule. Leave every file you touch a little cleaner than you found it.
- Refactor alongside features. When a feature passes through a problem area, include the clean-up in its estimate.
- Add tests first. Automated tests around risky code make it safe to change.
Technical debt is a business decision, not just an engineering one. Explain it in terms of speed, risk and cost, and it gets prioritised.
When a rewrite makes sense
Full rewrites are tempting and risky. Consider one only when the platform itself is unsupported, or when incremental change has been tried and is genuinely impossible. Even then, replace the system piece by piece, routing traffic to new components as they are ready, rather than switching over in one go.