Strategic refactoring: when to act and how to justify the investment
ArchitectureTechnical Debt

Strategic refactoring: when to act and how to justify the investment

Refactoring isn't a technical whim: it's a business decision. How to quantify the cost of doing nothing, choose where to act and win leadership's backing.

QMates· Software Advisory10 April 202610 min read

Every software organisation has the same conversation, over and over: developers ask for time to "tidy up the code", leadership asks for features and visible results. Refactoring is seen as an internal, technical activity that's hard to justify. The result is that it keeps getting put off, until the cost of doing nothing becomes impossible to ignore.

This is the point where many companies find themselves: a system that works, but that's holding them back. Every new feature takes longer than expected, every release brings anxiety, every new developer takes weeks to become productive. The cost of change keeps rising, the roadmap becomes unreliable, and leadership senses things are slowing down without understanding why.

Strategic refactoring treats this as a business decision: you choose where to act based on impact, quantify the cost of doing nothing, and measure the return.

Why refactoring always gets put off

The problem is one of language. When a CTO says "we need to do some refactoring", the CEO hears "we want to spend time and money without producing anything visible". The perception is that refactoring is a cost with no return, a technical whim, a request for time to "fix things" that should have been done properly in the first place.

That perception is wrong, but understandable. Technical debt is invisible from the outside: the software works, customers use it, features get delivered. The fact that every feature costs three times what it should, or that a release needs three days of manual testing, isn't visible to anyone who doesn't work on the code.

You don't justify refactoring by talking about code. You justify it by talking about costs, risks and missed opportunities.

How to quantify the cost of doing nothing

Technical debt has a cost. It's not a theoretical cost: it's one you pay every day, in time, risk and missed opportunity. According to McKinsey, technical debt that hasn't yet been addressed represents an average of 20–40% of the value of a company's entire IT estate.

Making it visible to leadership takes concrete numbers:

  • Release time: how long does it take from the moment a feature is ready to the moment it's in production? If the answer is "more than a week", there's architectural friction. Multiplying that time by the team's hourly cost gives you the cost of the debt on every release
  • Coordination cost: how many meetings, cross-team code reviews and alignment sessions does every change need? Every hour spent coordinating is an hour not spent producing value
  • Recurring bugs: how many production incidents are caused by parts of the code that are known to be problematic? Every incident has a cost: diagnosis time, fix time, customer impact, team stress
  • Slow onboarding: how long does it take a new developer to become productive? If the answer is "more than a month", the codebase is too complex. Every month of onboarding is a month's salary spent on training instead of delivery

The Consistency Impact Calculator helps you estimate these costs. Once the numbers are on the table, the conversation changes: it's no longer about "tidying up the code" but about "cutting a cost we're paying every day".

Where to act: not everywhere

Strategic refactoring isn't "rewriting everything you don't like". It's choosing, surgically, where to act to get the maximum impact for the minimum risk.

The selection criterion is simple: act where the friction is highest and the change is most frequent.

A badly written module that never changes isn't a priority: the cost of the debt is low because nobody pays it. A module that changes every week and generates bugs with every change is the priority: the cost of the debt gets paid continuously.

Three questions to identify the right starting point:

  1. Which part of the code gets changed most often? (frequency of change)
  2. Which part generates the most bugs or needs the most coordination? (friction)
  3. Which part blocks the most teams or slows down the most releases? (impact on delivery)

The intersection of these three answers is where you start.

How to refactor without stopping delivery

The most common fear is that refactoring will bring feature delivery to a halt. That fear is justified if you treat refactoring as a separate project: "for three months we won't ship features, we'll fix the code". This approach fails almost every time, because the business can't wait three months and the team loses context.

The approach that works is incremental refactoring, built into day-to-day work:

  • Before adding a feature: improve the structure of the module that will host it. Refactoring prepares the ground; the feature becomes easier to build
  • While making a change: leave the code in a better state than you found it (the boy scout rule). Small, continuous improvements, never big rewrites
  • After an incident: every production bug is an opportunity to improve the piece of code that caused it. The fix isn't just about resolving the symptom; it's about strengthening the structure

This approach produces gradual but steady results, without ever interrupting delivery. The team keeps shipping features while the code keeps improving.

Refactoring vs rewriting: why refactoring wins

A rewrite is the ultimate temptation: "this code is beyond saving, let's start from scratch". As we discussed in our article on evolutionary architecture, full rewrites fail at an alarming rate, because the old system contains years of business logic that nobody remembers.

Strategic refactoring has three decisive advantages over a rewrite:

  • It's incremental: you improve one piece at a time, without ever throwing everything away. The risk stays contained because every step is small and reversible
  • It's continuous: it isn't a project with a start and an end; it's an ongoing practice. The code keeps improving, the debt doesn't build up
  • It doesn't interrupt delivery: the team keeps producing visible value while the system improves beneath the surface

A rewrite only makes sense in one case: when the system is so compromised that the cost of every change exceeds the cost of rebuilding it. That's rare, and when it does happen, strategic refactoring done in time could have prevented it.

What to do in practice

Three steps to get started.

On Targeted Intervention engagements, the first question we ask isn't "what should we refactor?" but "where is delivery actually slowing down?" The 20% of the code causing 80% of the problems is almost always easy to spot: it's the part everyone avoids touching, where pull requests take days instead of hours, where every change produces unexpected bugs. That's where we act first. Refactoring spread thinly everywhere doesn't move the needle — refactoring concentrated on the system's hotspots does.

  1. Quantify the current cost of the debt: measure average release time, the number of recurring bugs, onboarding time. Put these numbers in front of leadership. Numbers change the conversation
  2. Choose a single module: the point with the most friction and the highest frequency of change. Not the biggest one: the most painful one. Define a contained intervention with a measurable outcome (release time cut by 30%, bugs halved, one fewer team involved)
  3. Build refactoring into the flow: don't create a separate project. Dedicate 20% of every sprint's time to improving the structure of the code in the area you're already working on. It's sustainable, it's continuous, and it produces visible results within a few weeks

If the debt runs deep and the system's boundaries aren't clear, a Targeted Intervention engagement can find the point of leverage and get things moving again. The investment starts paying for itself with the first feature that ships faster.

Tell us where you're stuck

A fragile prototype, a burdensome legacy codebase or unpredictable delivery: that's where we start

We use your data to respond to your request. Read our Privacy Policy.