· nervico-team · software-development  Â· 11 min read

When to Rewrite vs Refactor: The Most Expensive Decision in Software

A decision framework for rewriting vs refactoring: the second system effect, incremental migration strategies, the strangler fig pattern, and real examples of successes and failures.

A decision framework for rewriting vs refactoring: the second system effect, incremental migration strategies, the strangler fig pattern, and real examples of successes and failures.

“Let us throw everything away and start from scratch.” If you have worked in technology long enough, you have heard this phrase. You may have even said it yourself. It is understandable: when a codebase has become an obstacle, the temptation to start clean is enormous.

But software history is full of rewrites that failed spectacularly. Netscape rewrote its browser from scratch and it cost them 3 years and the loss of their market leadership to Internet Explorer. Borland rewrote their development environment and never recovered their position.

This does not mean that rewriting is always wrong. It means it is a decision that needs rigorous analysis, not an emotional reaction to frustration with the existing code.

In this guide you will find a framework for deciding when to refactor and when to rewrite, the patterns that work for incremental migrations, and the mistakes that cause rewrites to fail.

Why the Rewrite Is Tempting

The Blank Canvas Illusion

The current codebase has years of accumulated decisions: some good, some bad, many that made sense at the time but no longer do. Developers look at it and see a maze of exceptions, workarounds, and business logic buried in unexpected places.

New code, on the other hand, is imaginary. And the imaginary is always perfect. When you think about the rewrite, you imagine a clean codebase, well-structured, with current best practices. What you do not imagine are the 3,000 bugs that were discovered and fixed in the current system and that you need to reimplement in the new one.

Joel Spolsky called it “the single worst strategic mistake that any software company can make.” Not because rewriting is always bad, but because the decision is made with incomplete information and unrealistic expectations.

The Second System Effect

Fred Brooks described this phenomenon in “The Mythical Man-Month.” A team’s second system tends to be overloaded, overengineered, and painfully complex.

Why it happens:

The first system was built with humility. The team had limited resources, limited time, and genuine fear of failure. Every feature earned its place. Every decision was tempered by necessity.

The second system is built with confidence. And confidence tends to add features. “Since we are rewriting, let us add that feature we always wanted.” “Let us take the opportunity to also change the database.” “Let us implement that architecture pattern we read about in a blog.”

The typical result:

  • The project takes far longer than expected to reach feature parity with the original system
  • While developing the new system, new architectural problems emerge that the original system did not have
  • Resources are split between maintaining the old system and building the new one, prolonging the process
  • The team discovers that many of the “bad decisions” in the original system were actually necessary adaptations to real-world requirements

When Refactoring Makes Sense

The Fundamental Architecture Is Sound

If the system’s base architecture is correct but the implementation has problems, refactoring is almost always the right option. You can improve code quality, reduce complexity, and pay down technical debt without losing the structure that works.

Signals that the architecture is sound:

  • The system can accommodate new features, albeit with more effort than desirable
  • The main problems are code quality issues, not structural design flaws
  • Performance can be improved with targeted optimizations, not requiring a paradigm shift
  • Boundaries between modules or components are reasonably clear

Incremental Refactoring: The Boy Scout Rule

“Leave the code a little better than you found it.” Every time a developer touches a file, they improve something small: rename a confusing variable, extract a function, simplify a condition.

Advantages:

  • Practically zero risk (small, testable changes)
  • Does not require a dedicated project or special budget
  • Benefits accumulate over time
  • Keeps the team in touch with code quality

Limitation: It works for improving general quality but does not solve deep architectural problems.

Planned Refactoring

For larger problems than the Boy Scout rule can address, you need dedicated refactoring sessions.

How to do it:

  1. Identify the code areas that cause the most pain (those that generate the most bugs, those that require the most time to change)
  2. Define the improvement objective in concrete terms
  3. Write tests covering current behavior before refactoring
  4. Refactor in small, testable steps
  5. Verify that behavior has not changed

Recommended proportion: Reserve between 15% and 20% of team time for technical improvements. It is not a luxury, it is preventive maintenance.

When Rewriting Makes Sense

The Underlying Technology Is Obsolete

If your system is built on technology that no longer has support, does not receive security updates, and you cannot hire people who know it, the rewrite may be inevitable.

Real examples:

  • Flash applications after Adobe stopped supporting it
  • COBOL systems when the team maintaining them retired
  • Applications dependent on third-party APIs that were discontinued

Important nuance: “The technology is old” is not the same as “the technology is obsolete.” PHP 5 is old but can be incrementally migrated to PHP 8. COBOL without a team to maintain it is a different problem.

The Business Has Changed Radically

If the business model has changed so much that the current software cannot adapt, even with extensive refactoring, the rewrite makes sense.

Example: A company that moved from selling licenses to a multi-tenant SaaS model. If the original architecture assumed individual installations, the number of changes needed may justify starting over with a multi-tenant architecture from the ground up.

The System Is Small Enough

If you can rewrite the system in weeks, not months, the risk is manageable. For small systems, the cost of a rewrite may be less than the cost of understanding and refactoring complex legacy code.

Practical rule: If the system has fewer than 50,000 lines of code and a small team can rewrite it in 6-8 weeks, the rewrite may be viable.

There Are No Tests and the Code Is Incomprehensible

If the codebase has no tests, no documentation, and the original team is not available to explain decisions, refactoring becomes an archaeological exercise without a guide.

In these cases, the rewrite may be faster and safer than trying to understand and modify code nobody comprehends. But be careful: you need to understand current behavior before rewriting, or you will reproduce the same errors.

The Strangler Fig Pattern: Incremental Migration

What It Is and How It Works

The Strangler Fig pattern, introduced by Martin Fowler, is the safest alternative to a total rewrite. Instead of replacing the entire system at once, you build the new system around the old one and migrate functionality gradually.

The step-by-step process:

  1. Identify a component of the system you want to replace. Start with one that is relatively independent and has clear boundaries.

  2. Build the replacement alongside the current system. The new component runs in parallel with the old one.

  3. Redirect traffic gradually. First a small percentage of requests go to the new component. If it works, increase. If it fails, roll back.

  4. When all traffic goes to the new component, remove the old one.

  5. Repeat for the next component.

Why It Works

Controlled risk: If the new component fails, the old one still works. You do not bet everything on a single card.

Continuous value delivery: You do not need to wait for the complete migration to finish to see benefits. Each migrated component improves the system.

Progressive learning: Each migration teaches you something. The second is better than the first. The fifth is much better than the first.

Industry data: Teams using Strangler Fig achieve 20-30% faster delivery cycles within the first 6-12 months.

When It Does Not Work

The Strangler Fig works best for large systems with relatively independent components. For small, highly coupled systems, the effort of implementing the pattern may exceed a direct rewrite.

It also does not work if you cannot identify clear boundaries between components. If everything is so coupled that you cannot extract one part without touching everything else, you first need decoupling work within the monolith.

Decision Framework

Step 1: Evaluate the Architecture

Question: can the system’s fundamental architecture support the evolution the business needs?

  • Yes: Refactor. The architecture is your most valuable asset.
  • No: Consider incremental migration or rewrite.

Step 2: Evaluate the Size

Question: how long would a complete rewrite take?

  • Less than 2 months: The rewrite is viable if the current architecture does not serve.
  • 2-6 months: Incremental migration (Strangler Fig) is preferable.
  • More than 6 months: A total rewrite carries very high risk. Incremental migration or refactoring.

Step 3: Evaluate Knowledge

Question: does the team understand the current system well enough to refactor it safely?

  • Yes: Refactoring is safer.
  • No, but there are tests: Refactor with caution, relying on the tests.
  • No, and there are no tests: Consider the rewrite, but first document the current behavior.

Step 4: Evaluate the Business

Question: can the business afford the time each option requires?

  • Refactoring: Gradual improvement, the system keeps working while it improves.
  • Incremental migration: Medium time, the system improves in parts.
  • Total rewrite: Long time, and during that time you maintain two systems.

Mistakes That Cause Rewrites to Fail

Mistake 1: Not Reaching Feature Parity

The most common mistake. The team starts the rewrite with enthusiasm, adds new features, and never reaches parity with the original system. Users cannot migrate because they are missing things they already had.

Solution: Define feature parity as the first milestone. Nothing new until the new system does everything the old one does.

Mistake 2: Maintaining Two Systems Too Long

While the rewrite progresses, the old system needs maintenance. Bugs, security patches, minor changes. The team splits and both systems suffer.

Solution: Set a maximum deadline for the migration. If you cannot migrate within that deadline, the rewrite is probably not viable.

Mistake 3: Not Understanding Why the Old Code Is the Way It Is

Many of the “bad decisions” in the old codebase are actually adaptations to real-world requirements that are not documented. A strange if statement in the billing code may be handling a special case that affects an important client.

Solution: Before rewriting a module, talk to users and the team that maintains it. Understand not just what the code does, but why it does it that way.

Mistake 4: Overengineering the New System

The second system effect. “Since we are rewriting, let us make it perfect.” Perfection is the enemy of delivery.

Solution: The new system should be better than the old one in the areas that matter, not perfect in everything. Prioritize the improvements that generate the most business impact.

Mistake 5: Underestimating Data Migration Effort

Data from the old system has years of history, inconsistent formats, and special cases. Migrating it to the new system is a project in itself.

Solution: Include data migration as an independent line item in the project plan, with its own budget and timeline.

Practical Guide: What to Do Tomorrow

If you are considering a rewrite, before making the decision:

  1. Take an inventory. Document what the current system does. Not the code, but the functional behavior visible to users.

  2. Identify the real pain points. Not “the code is ugly.” But “every time we add a billing rule it takes 3 weeks instead of 3 days.”

  3. Evaluate incremental alternatives. For each pain point, ask yourself: can this be solved with refactoring? With a partial migration?

  4. Calculate the real cost. Not just the development cost of the new system. Also the cost of maintaining two systems, the cost of data migration, and the cost of training.

  5. Do a proof of concept. Before committing to the full rewrite, migrate one component using Strangler Fig. If that component goes well, continue. If not, reconsider.

Conclusion

The decision between rewriting and refactoring is not emotional. It is a business decision that requires data, risk analysis, and realistic expectations.

Keys to deciding well:

  1. Refactoring is almost always safer than rewriting. The burden of proof is on the rewrite, not on refactoring.
  2. If you need to rewrite, use Strangler Fig. Incremental migration dramatically reduces risk.
  3. Beware the second system effect. The new system should be better, not perfect.
  4. Do not rewrite what you do not understand. First document, then decide.
  5. 80% of the time, incremental improvement is the right option. Successful rewrites are the exception, not the rule.

The temptation to “start from scratch” is universal. But the best engineering teams resist that temptation and find ways to improve what they have without risking everything.


Is your team stuck between maintaining an old system and the temptation to rewrite?

In a free technical audit we can help you:

  • Evaluate whether your case justifies refactoring, incremental migration, or rewrite
  • Identify the components that have the most impact if improved first
  • Design an incremental migration plan with Strangler Fig if needed
  • Calculate the real cost of each alternative so the decision is data-driven

No commitments, no PowerPoints. Just an honest technical diagnosis.

Request free technical audit

Back to Blog

Related Posts

View All Posts »