Independent insights on SaaS, technology, marketing and business Guidelines Editorial Policy
Software Development

Managing Technical Debt Without Stopping the Roadmap

Technical debt arguments fail because they are framed as engineering preferences. Here is how to make them a business decision instead.

Every engineering team wants to pay down technical debt and almost none of them get sustained time to do it. The reason is usually framing: the request arrives as “the code is bad and we would like to fix it”, which is indistinguishable from a preference.

Debt that gets paid down is debt that someone has attached a cost to.

Not all debt is the same

Four categories, each needing a different response.

Deliberate and documented. A shortcut taken knowingly to hit a date, with the trade-off recorded. This is debt in the original sense — borrowed time with a known repayment. It is healthy in moderation.

Accidental. The design made sense given what was known then; the requirements changed. Nobody did anything wrong, and the code still needs restructuring.

Rot. Dependencies out of date, deprecated APIs, unsupported runtime versions. This accrues whether or not anyone touches the code, and it eventually becomes a security problem rather than a quality one.

Sloppiness. Debt from inattention rather than trade-off. The only category worth preventing culturally rather than scheduling.

Conflating these in one backlog is why debt conversations go nowhere. Rot has a deadline; accidental debt has a cost; sloppiness has a process fix.

Build an inventory with costs attached

A list of complaints is not an inventory. For each item, record: what it is, which work it slows down, how much it slows that work, and what fixing it would cost.

The slowdown is the number that matters, and it can be estimated honestly. “Every change to billing takes three days instead of one because the logic is duplicated in four places” is a claim a product manager can act on. “The billing code is a mess” is not.

Where it can be measured, measure it: time to make a typical change, incident frequency in that area, onboarding time for a new engineer, build and test duration.

Prioritise by interest rate, not by size

Debt costs you every time you touch the affected code. Debt in a module nobody has changed in two years costs nothing, however ugly it is.

The ranking that works is change frequency multiplied by friction per change. A moderately messy module touched weekly is a far better target than a terrible module touched annually. Version control history gives you the change frequency for free.

The exception is risk-based debt: an unsupported dependency with a known vulnerability needs fixing regardless of how often the code changes.

Making the business case

Translate into the units the business already uses.

  • Velocity. “Changes in this area take three times longer. We ship roughly one feature a quarter here; fixing this would let us ship three.”
  • Risk. “This runtime loses security support in eight months. After that we are running unpatched software, which will also fail our next SOC 2 audit.”
  • Cost. “This service is over-provisioned because it cannot scale horizontally. Fixing it saves a measurable amount a month.”
  • Retention. Harder to quantify and real. Engineers leave codebases that are miserable to work in, and replacement is expensive.

Frame the ask as an investment with a return and a timeframe, not as a cleanup. “Six engineer-weeks, after which billing changes take one day instead of three” is a proposal. “We need a refactoring sprint” is not.

Four strategies for finding the time

A fixed share of capacity. Twenty percent of every sprint, protected. Simple and sustainable; the risk is that it becomes the first thing cut under pressure. Protect it explicitly or it will not survive a bad quarter.

The boy scout rule. Leave code better than you found it, as part of normal work. Excellent for incremental improvement, useless for anything requiring coordinated change.

Debt attached to features. When a feature touches a messy area, the cleanup is scoped into the feature. This is the easiest to get approved because the cost is visible in context, and it naturally targets the code that is actually being changed.

Dedicated projects. For large structural work — a framework upgrade, a database migration — that cannot be done incrementally. Needs explicit prioritisation against features and a defined end state.

Most healthy teams run a combination: the boy scout rule continuously, debt attached to features routinely, and a dedicated project once or twice a year.

Preventing the next round

Three practices reduce accumulation substantially.

Record deliberate debt at the moment it is taken. A ticket, linked from a code comment, saying what was skipped and why. Undocumented shortcuts become accidental debt within six months, once the people who knew have moved on.

Keep dependencies current automatically. Automated dependency updates running weekly turn a painful annual upgrade into routine small changes. Rot is the easiest debt to prevent and the most commonly neglected.

Set standards mechanically. Linting, formatting, test coverage thresholds and architecture checks in CI. Standards enforced by review depend on who reviews; standards enforced by the pipeline do not.

Knowing when not to fix it

Three cases where leaving it is correct.

Code scheduled for replacement. Refactoring something you will delete in six months is pure waste.

Code nobody touches. Ugly and stable is an acceptable state. The cost of debt is realised only on change.

Code where the fix is riskier than the problem. Rewriting a critical system that works, with no tests and no original authors available, often trades a known inconvenience for an unknown outage.

Tracking it over time

A few indicators show whether the situation is improving: median time from commit to production, change failure rate, time to make a representative change in the worst areas, and the number of dependencies more than a major version behind.

Avoid composite “debt scores” from static analysis tools. They are easy to game, hard to interpret and rarely correlate with the friction engineers actually feel.

The conversation that usually works

Most successful debt proposals follow a similar shape, and it is worth copying.

Start with something the business already feels. Not “the payments module is poorly structured” but “the three payment bugs last quarter all came from the same duplicated logic”.

Quantify the ongoing cost in time or money. “Each change here takes four days instead of one, and we make about two a month.”

Propose a bounded piece of work with a defined outcome. Not “refactor payments” but “consolidate the four copies of the pricing calculation into one, three engineer-weeks, after which changes take a day.”

Offer a way to verify it worked. “Measure the time for the next three payment changes.”

What fails is the open-ended request. “We need to spend a quarter on debt” invites a no, because nobody can tell what they would be buying.

Debt in acquired or inherited systems

Inheriting a codebase — through acquisition, a departing team or a contractor handover — is a special case, because nobody knows which debt is dangerous.

Three steps help. First, establish what the system does from the outside: write characterisation tests that capture current behaviour, correct or not, so changes can be made safely. Second, find the parts that change most often from version control history; that is where the debt costs something. Third, resist the urge to rewrite in the first six months, when understanding is lowest and confidence is highest.

The common failure is a well-intentioned rewrite begun before anyone understood why the original was strange. The strangeness is usually a requirement nobody documented.

Frequently asked questions

How much time should go to technical debt?

Fifteen to twenty percent of engineering capacity is a common sustainable figure. Teams carrying heavy debt may need more for a period; teams in a greenfield phase may need less.

Should technical debt live in the product backlog?

Yes, with the same visibility and prioritisation as features. A separate hidden backlog is a backlog nobody schedules.

Is a rewrite ever the right answer?

Occasionally, and far less often than engineers propose it. Rewrites routinely take two to three times the estimate and reintroduce bugs fixed years ago. Incremental replacement behind a stable interface is usually the better route.

How do I explain debt to a non-technical stakeholder?

Use the time cost directly. “This change would take two days in a clean system and takes a week here” is immediately understood and needs no metaphor.

Leave a Reply

Your email address will not be published. Required fields are marked *