Running a tech debt remediation programme across a multi-service platform, without a feature freezeLeading a technical debt clean-up without stopping the business
Years of shortcuts across a multi-service platform were slowing delivery and making releases risky. An evidence-led audit, targeted test and pipeline work, and CI guardrails to stop the debt coming back.A multi-service platform had years of shortcuts weighing the team down. Here's how we prioritised the clean-up and actually saw it all the way through.
The problem
The platform is made up of several services, built over years by different teams under different deadlines. Nobody set out to make a mess. It’s what happens when shipping matters more than tidiness for long enough, and “we’ll fix it later” never arrives.
By the time I got involved, the cost showed up everywhere. Features that should have taken days took weeks, because much of the effort went on working around old decisions. Test coverage was thinnest in exactly the areas that changed most often. Coupling between services meant a change in one could break another in ways nobody predicted, and there wasn’t the logging or tracing to see quickly where a failure had actually started. Releases carried more risk than they should have, and the engineers, who feel this most, were frustrated.
The business knew something had to change, but it didn’t have an honest picture of where the real problems were. It also had no appetite for a six-month feature freeze or a rewrite, which is rarely the right answer anyway.
What we did
I started with an audit based on evidence, not on building a case for a rewrite. I went through the platform service by service with the engineers who work in each one, looking at where bugs kept coming back, where changes took longest, and where people were visibly working around problems rather than through them.
That produced a real list, and then came ruthless prioritisation. Some debt costs the business time and money every week. Some is just untidy and can be left alone indefinitely. I ranked the list by where the pain actually landed, not by what was theoretically the “correct” way to build things. The shortlist centred on two areas:
- Test coverage around the fragile, high-change areas. We wrote tests that pinned down current behaviour before anyone changed it, so the fixes themselves didn’t introduce new regressions.
- CI/CD and observability. We made pipelines faster and more reliable so engineers got feedback in minutes, and added logging and tracing across services so failures showed up quickly, with their real origin visible.
I didn’t hand over a report and leave. I stayed on to run the programme alongside the engineering team, sequencing the work so the riskiest, highest-value fixes went first, and interleaving it with normal feature work. The business kept shipping to customers throughout, which was non-negotiable.
Debt creeps back unless there’s a shared standard for what’s acceptable to ship under pressure. So we added lightweight guardrails that don’t slow the team down:
- A definition of done that includes tests, so “shipped” means the same thing to everyone.
- Decision records for deliberate shortcuts, so they’re tracked and revisited rather than forgotten.
- Linting and CI gates that block known bad patterns automatically, rather than relying on someone catching them in review.
Where things landed
Feature work no longer starts with untangling old decisions, and the roadmap moves again instead of stalling.
Releases carry less risk. There are fewer regressions turning up in unrelated services, and when something does fail, tracing shows where it started instead of leaving the team to work it out late at night.
The biggest shift is in the team. Engineers who were frustrated and starting to look elsewhere are engaged again, because the day-to-day experience of working in the codebase has genuinely improved. With the guardrails in place, it’s staying that way.
The problem
This is a large platform made up of several services that work together, built up over a good few years by different teams under different pressures. Nobody set out to make a mess. It’s what happens naturally when a business is growing fast and every deadline matters more than the tidiness of the code behind it. Shortcuts get taken with every intention of fixing them later, and later never quite arrives.
By the time I got involved, the cost of all that was showing up everywhere. Features that should have taken days were taking weeks, because half the effort went into working around old decisions rather than building the new thing. Releases carried more risk than they should have, because nobody was entirely sure what a change in one part of the system might break in another. And the engineers, who are generally the ones who feel this most acutely, were frustrated. Good people don’t enjoy fighting the codebase every day.
The business knew something needed to happen. What it didn’t have was a clear, honest picture of where the real problems were, and it certainly didn’t have the appetite to down tools for six months to fix everything at once. That’s rarely the right answer anyway. A business still has to ship things while it cleans up.
What we did
The first step was an honest audit, and I mean genuinely honest, not a box-ticking exercise designed to justify a big rewrite. I went through the platform service by service, talking to the engineers actually working in each part, looking at where bugs kept recurring, where changes took longest, and where people were visibly working around problems rather than through them.
That gave us a real list, and the next job was ruthless prioritisation. Not everything that’s messy is worth fixing. Some technical debt is genuinely costing the business time and money every week. Some of it is just untidy and can be left alone indefinitely without anyone suffering for it. I built a shortlist based on where the pain was actually landing, not on what was theoretically the “correct” way to build things.
Then came the part that matters most and gets skipped most often: actually leading the remediation. I didn’t hand over a report and leave. I stayed on to run the programme, working alongside the existing engineering team, sequencing the fixes so the riskiest, highest-value ones went first, and making sure the clean-up happened alongside normal feature work rather than instead of it. The business kept shipping to customers the whole way through, which was non-negotiable.
Part of the job was also cultural. Technical debt creeps back in unless the team has a shared, realistic standard for what’s acceptable to ship under pressure and what isn’t. So alongside the fixes, we put lighter habits in place, ones that don’t slow the team down but stop the same problems quietly rebuilding themselves.
Where things landed
The clearest sign of progress is what the team spends its time on now. Feature work that used to involve untangling old decisions first now mostly doesn’t. That’s not a minor convenience, it’s the difference between a roadmap that moves and one that stalls.
Releases carry less anxiety too. Fewer surprises turning up from unrelated corners of the system, and fewer late nights spent working out what a small change accidentally broke elsewhere.
Perhaps the biggest shift is one you’d only notice by talking to the team: engineers who were frustrated and starting to look elsewhere are engaged again, because the daily experience of doing the work has genuinely improved. Fixing technical debt properly isn’t glamorous, but it’s the kind of work that pays the business back every single week afterwards.
Facing something similar?
If this sounds like where your business is right now, let's talk about it.
Get in touch