Your technical debt has a solution (and it's not rewriting)

29/08/2025
several disorganized Lego pieces

On average, development teams spend a third of their time dealing with technical debt instead of building new things. When the situation becomes unsustainable, the temptation is to scrap everything and start from scratch. But complete rewrites are one of the riskiest bets in software development. The question is: is there another way?

Your product works. Users use it, the business generates revenue, the team releases features. But each new feature takes longer than it should. Bugs appear where no one expects them.

There are parts of the code that nobody wants to touch because it's not very clear what they do or why.

That's technical debt. And if that sounds familiar, you're not alone. According to Stripe data, development teams spend an average of 33% of their time managing technical debt instead of building new things. That's no small problem.

The natural reaction when debt piles up is to think about rewriting. Starting from scratch, doing it "right this time." But complete rewrites are one of the riskiest decisions a product team can make. They take longer than expected, cost more than budgeted, and, in the meantime, the revenue-generating product is frozen.

There is another way to do it.

What is technical debt (and what it is not)

The term was coined by Ward Cunningham in 1992, and the financial analogy remains the best way to understand it. Just as you can take out a loan to move faster and then pay it back with interest, in development you can take technical shortcuts to deliver earlier in exchange for a future cost.

The problem arises when that future cost isn't paid. Interest accrues: the code becomes harder to understand, more fragile, and slower to modify. What used to take a day now takes a week.

But not all old or imperfect code is technical debt. It's important to distinguish:

Deliberate debt. A shortcut was consciously taken to meet a deadline, with the intention of fixing it later. It is legitimate if managed properly.

Accidental debt. The team didn't realize they were creating debt. There was a lack of experience, context, or things were simply done hastily. This is the most common type.

Environment debt. Dependencies become obsolete, frameworks evolve, best practices change. Code that was correct three years ago may be considered debt today even if no one has done anything wrong.

Simply bad code. It's not a problem, it's a flaw. The difference matters because the solution is different.

Identifying what type of debt you have is the first step in deciding how to act.

How to know if your technical debt is a real problem

Every codebase has some debt. It's inevitable and, to a certain extent, acceptable. The question isn't whether it exists, but whether it's holding the business back.

There are clear signs:

New features are taking longer and longer to develop. If a year ago an average feature took two weeks and now it takes six, something is wrong. And it's rarely because the team is working less.

Bugs multiply in unexpected areas. You modify one module and another, seemingly unrelated one, breaks. This usually indicates hidden dependencies, a lack of tests, or undocumented coupling.

Onboarding new developers is slow. If a senior developer needs more than a month to become productive in your codebase, debt is acting as a barrier to entry.

There are parts of the code that nobody wants to touch. The famous "it works, don't touch it." When a team actively avoids modifying certain areas, it's a sign that the debt in that area is high, and the perceived risk is also high.

The technical team is asking to "stop to refactor." If the team feels it can't move forward without cleaning up first, the debt is already affecting morale and speed.

If you recognize three or more of these signs, technical debt is probably already costing you money. Not in the abstract: in lost development hours, unreleased features, and frustrated talent.

Why rewriting is almost never the answer

When debt becomes unbearable, the temptation is to throw everything away and start from scratch. Joel Spolsky, co-founder of Stack Overflow, called it "the worst strategic mistake a software company can make." Perhaps he exaggerated, but not by much.

Complete rewrites fail for several reasons:

They underestimate the complexity of the current system. The existing code, however ugly it may be, contains years of decisions, bug fixes, and edge cases that no one remembers. Rewriting means rediscovering all of that.

They halt the product. While the team rewrites it, the current product doesn't progress. Competitors do. Users grow impatient. The business stagnates.

They take much longer than expected. It's an almost universal pattern: the rewrite that was supposed to take six months ends up taking eighteen. And when it's finally finished, the requirements have changed.

They create their own debt. A team that rewrites under pressure makes the same mistakes it made the first time, or new ones. The debt doesn't disappear: it resets.

There are exceptions. If the underlying technology is completely obsolete—an unsupported framework, a language without a community, an architecture that doesn't allow for any scalability—rewriting might be the only option. But even in those cases, a gradual migration usually works better than a big bang.

The alternative: gradual reduction

If rewriting isn't the solution, what is? An incremental approach. Continuously reducing debt, integrated into daily work, without halting product development.

It's not as epic as a rewrite, but it works.

Identify where it hurts the most

Not all debt has the same impact. A legacy module that no one touches and that works is debt, but it's not urgent. A module that the team modifies every week and that constantly generates bugs is debt that's costing money now.

Prioritize business impact, not code elegance. The question isn't "Which part of the code is the ugliest?" but "Which part of the code is holding us back the most?"

Establish a debt budget

The 80/20 rule works well as a starting point: 80% of the team's time is dedicated to features and product, and 20% to reducing technical debt. Some teams use one full sprint out of every five for refactoring tasks. Others set aside one day a week.

The important thing is that it be explicit. If debt reduction isn't in the plan, it's not going to happen. Good intentions don't survive the pressure of a roadmap.

Refactor in context

The most efficient way to reduce debt is to do it alongside product work. You're going to modify a module to add a feature: take the opportunity to clean up what you find. You don't need a separate refactoring ticket. You don't need to ask for permission.

It's what Martin Fowler calls the "boy scout rule": leave the code a little better than you found it. Not perfect. Just a little better. Multiplied by dozens of commits a month, the cumulative effect is enormous.

Add tests where there aren't any.

Technical debt without tests is blind debt: you don't know what will break when you touch it. Before refactoring a critical area, write tests that cover the current behavior. You don't need 100% coverage; you need enough tests to know you're not breaking anything.

This is especially important in legacy systems where documentation is scarce or nonexistent. Tests act as living documentation of expected behavior.

It continuously modernizes its facilities

Don't let dependencies fall three major versions behind. Updating a library when it's one release behind is easy. Updating one that hasn't been touched in four years is a project in itself.

Tools like Dependabot or Renovate automate update detection and generate pull requests that you can review and merge with low risk. It's preventative maintenance: unglamorous but highly effective.

Document the decisions, not just the code.

Much technical debt stems from decisions that made sense at the time but whose context has been lost. Why was this architecture chosen? Why was this module designed this way? Without that context, the next developer who touches it won't know if they're dealing with a deliberate decision or a mistake.

Architecture Decision Records (ADRs) are a simple way to document these decisions. There's no need to write an essay: simply explain what was decided, why, and what alternatives were discarded.

When do you need external help?

Gradual downsizing works when the team has the capacity to absorb it. But there are situations where the team is so busy keeping the system afloat that it has no room to improve anything.

It's the technical equivalent of being so indebted you can't even pay the interest. In these cases, an external team with experience in systems refactoring and modernization can resolve the situation: assessing the debt, prioritizing interventions, and implementing them without halting product development.

It's not a luxury. It's a business decision. The cost of inaction—features that aren't released, developers who leave, recurring incidents—is usually higher than the cost of intervention.

What really matters

Technical debt is not a failure. It's a natural consequence of building software in an environment where deadlines exist, requirements change, and resources are finite. Every company with a digital product has technical debt. The difference lies in how it's managed.

Ignoring it is expensive. Rewriting it is risky. What works is something less flashy but more solid: making the debt visible, measuring it, prioritizing it, and constantly reducing it.

You don't need to shut everything down to fix your codebase. You need to incorporate debt reduction as part of the job, not as an exception. And if the hole is already too big for your team, asking for help early is smarter than waiting until the situation is irreversible.