7 min read
saas
arquitectura
refactoring
legacy
decisão-técnica

Rewriting a Legacy SaaS: When It's Worth It and How to Decide

Rewriting a legacy SaaS product is expensive and risky. Discover the real technical and commercial criteria for making the right decision, with concrete examples and pitfalls to avoid.

Rewriting a Legacy SaaS: When It's Worth It and How to Decide

Complete rewrites fail in 70% of cases and take 2, 3x longer than estimated. The real question is: "what is actually broken?" Before rewriting, measure: current maintenance cost (€/month), feature delivery velocity (features/sprint), production bug rate (bugs/10k RPS), and customer satisfaction (NPS or churn). A rewrite only makes sense when these numbers are critical (>€50k/month maintenance, <2 features/sprint, >50 bugs/month in production, NPS <20 or churn >5%/month). The alternative is strategic parallel refactoring: modernise the stack by layers (data, API, UI) while keeping production live. It takes longer, but reduces risk from 70% to <10%. Real case: companies that stopped rewrites mid-way (GitLab, Stripe v1 vs v2 transition) moved to controlled incrementalism. Most never went back.


The True Cost of a Rewrite

Let's be direct. When you hear "the code is a mess, we need to rewrite", what's really happening is one of three scenarios:

  1. The codebase is old but stable. New developers take 3, 4 weeks to learn it. There's duplicated code, but nothing has exploded.
  2. The product is failing in real ways: P99 latencies above 1000ms, bug rate >10/day in production, new features take 3 sprints to ship.
  3. The stack is obsolete (Python 2, Rails 3, Backbone.js) and hampers talent recruitment.

Each one deserves a different approach. A complete rewrite only makes sense in the second scenario. And even then, it's risky.

Industry studies show that complete software rewrites have a success rate below 30%. Joel Spolsky wrote about this 20 years ago ("Things You Should Never Do"), and the pattern holds. Why?

When you rewrite, you implicitly redesign. That means "obvious" features from the old version disappear or get pushed to "phase 2". Customers see downtime or degradation. Team morale drops because they're rebuilding known functionality instead of innovating.

Meanwhile, the old version keeps having bugs discovered in production. The new version has no fixes for those bugs because the code is new. So the time to "feature parity" is a minimum of 18, 24 months on a non-trivial product (medium complexity: 100k lines, 20+ features, 50+ integrations).

Real costs of a rewrite (industry averages):

PhaseDurationCost (5-person engineering team)Risk
Analysis and design2 months€80kUnderestimating complexity
New MVP4 months€160kIncomplete feature parity
Data migration (hard part)3 months€120kData loss/corruption
Testing + fixes3 months€120kProduction bugs
Sunsetting old version2 months€80kUnexpected regressions
TOTAL14 months€560k50, 70% failure

And that's the optimistic case. If complexity is higher (many integrations, historically lots of data, complex business logic), multiply everything by 1.5x.


The Numbers That Define the Real Problem

Before any decision, measure this. Concrete data, not feelings.

1. Monthly maintenance cost

Track the time your team spends on "maintenance" versus "feature delivery". If 60% of the sprint is bugfixes, tech debt, and legacy support, you're a candidate. If it's 20%, you're not.

Formula: (hours spent on maintenance per sprint / total hours) × (team monthly cost)

Examples:

Stable SaaS: 15% maintenance = €12k/month on a €80k/month total team. Acceptable.

Degraded SaaS: 60% maintenance = €48k/month. Critical.

2. Feature delivery latency

How long does an "average" feature take from idea to production?

Healthy: 2, 3 sprints (10, 15 days)

Warning: 5, 6 sprints (25, 30 days)

Critical: 8+ sprints or indefinitely blocked

3. Production bug rate (normalised)

Bugs per 10k requests is a good metric because it scales with volume:

bugs_per_10k_rps = (bugs_in_production_per_week / avg_rps_per_week) × 10000

Healthy: <5 bugs/10k RPS

Warning: 10, 20 bugs/10k RPS

Critical: >30 bugs/10k RPS (every deployment is Russian roulette)

4. NPS or churn rate

If customers are leaving because the product is slow or constantly buggy, it's a symptom of a deeper problem.

NPS >50: Don't rewrite

NPS 20, 50: Watch carefully

NPS <20 or churn >5%/month: Rewrite is a legitimate option

5. Hiring capacity

If nobody wants to work on the old stack (Rails 3, Angular 1, PHP 5), you're at a real competitive disadvantage. This isn't tech vanity. Good engineers avoid genuinely broken legacy code because they know they'll get stuck.


The Pitfall: Why Rewrites Fail (Even With Good Intentions)

I've seen this in consulting several times.

Mistake 1: Ignoring feature parity

The old version has 15 years of features, some of them obscure. One customer uses "export to CSV with custom headers", another uses "bulk update via API v2", another has integrations with old systems. When the new version ships, 5 large customers discover that feature X disappeared. NPS drops. Revenue drops.

Mistake 2: Underestimating data migration

Data is the hardest part. Migrating 500GB of history with cross-references, without downtime? Expect 3 months, not 2 weeks. If there's corruption or inconsistency, you discover it in production. Meanwhile, support explodes.

Mistake 3: Two products in parallel = 2x cost

While you rewrite, someone needs to maintain the old version (bugs, security patches, urgent customer features). So: 2 teams, 2 repositories, 2 databases. This isn't parallel, it's a duel. It's always slower than expected.

Mistake 4: Underestimating complexity

"It'll be simple because we have a clear spec." It won't. There are always edge cases, forgotten integrations, business rules that only live in some customer's Excel spreadsheet. The new version will discover this too late.

Mistake 5: Go-live timing

If you plan go-live on a Wednesday in winter, and something breaks, support is on leave. Always. Go-live should be on a Tuesday morning, with everyone present and well-rested.


The Smart Alternative: Strategic Parallel Refactoring

Instead of rewriting everything, modernise by layers. It's slower initially, but dramatically reduces risk (from 70% to <10%).

Strategy: Strangler Fig Pattern

// Old version: Node.js + Express 3, no types, no tests
app.get('/api/users/:id', function(req, res) {
  db.query('SELECT * FROM users WHERE id = ' + req.params.id, 
    function(err, result) {
      if (err) res.json({error: true});
      else res.json(result);
    }
  );
});

// Phase 1: Put a modern proxy in front (Express 5 + TypeScript)
// Redirect endpoints one by one to new implementation
app.get('/api/users/:id', async (req: Request, res: Response) => {
  try {
    const userId = parseInt(req.params.id, 10);
    if (isNaN(userId)) {
      return res.status(400).json({ error: 'Invalid user ID' });
    }
    const user = await db.query(
      'SELECT id, email, name, created_at FROM users WHERE id = $1',
      [userId]
    );
    if (!user.rows.length) {
      return res.status(404).json({ error: 'User not found' });
    }
    res.json(user.rows[0]);
  } catch (err) {
    logger.error('GET /api/users/:id failed', { userId: req.params.id, err });
    res.status(500).json({ error: 'Internal server error' });
  }
});

// Phase 2: Gradually shift traffic to new implementation
// 5% traffic → 25% → 50% → 100% (over 3, 6 months)
// If error rate jumps by 2%, shift back to old version

Advantages:

  1. Rollback is trivial: If the new "/api/users/:id" implementation fails, redirect back to the old one in 5 minutes.
  2. Zero downtime: Customers don't know there's a migration happening.
  3. Real validation: Test the new implementation against real traffic before total commitment.
  4. Less burnout: The team isn't under pressure from a "big bang" go-live.

The trade-off is straightforward: it takes longer (12, 18 months vs 14, 18 months compressed). But with a 90%+ success rate versus 30%.


When It Really Is Worth Rewriting

If you've measured the numbers and the situation is critical, a rewrite might be the answer. But with conditions.

Rewrite makes sense if:

  1. Monthly maintenance cost is >€50k and trending upwards
  2. Bug rate is >30/10k RPS and directly affects revenue (big customers complaining)
  3. Feature delivery has slowed to <2 features/sprint and is the business bottleneck
  4. Stack is genuinely obsolete (security, support, hiring)
  5. You have dedicated budget (not stolen from feature delivery)
  6. You have time: 18, 24 months

Rewrite does NOT make sense if:

  1. The problem is team politics, not code
  2. You expect rewriting to solve business problems (it won't)
  3. You don't have a stable team (high churn = rewrite dies)
  4. Competition is fierce (you don't have 18 months to pause feature delivery)

Example: A Concrete Metric for Deciding

Here's a decision framework you can use:

REWRITE SCORE (0, 100)

[ ] Maintenance cost >€50k/month: +25 points
[ ] Feature delivery <2 features/sprint: +25 points
[ ] Bug rate >30/10k RPS: +20 points
[ ] NPS <20 or churn >5%/month: +15 points
[ ] Stack obsolete (support at EOL): +15 points

Scoring:
0, 30: Stick with current code. Cosmetic refactoring.
30, 60: Consider Strangler Fig Pattern. Modernise gradually.
60, 100: Rewrite is an option. But do Strangler Fig anyway, it's safer.

Conclusion

Rewriting a legacy SaaS is like renovating a house while living in it. It seems easier to demolish and rebuild, but the cost and risk are always greater than expected. Most of the time, the smart path is strategic refactoring with the Strangler Fig Pattern: parallel, incremental, and with rollback always available.

Measure the numbers (maintenance, latency, bugs, churn) before any decision. Decades of software engineering tell us that complete rewrites fail 70% of the time. The 30% that succeed had perfect preparation and timing.

If you're facing a similar problem, book a conversation at https://impact-origin.com/agendamento.

Impact OriginEnjoyed this article?At Impact Origin we help founders and teams build and scale custom software, from a startup MVP to the next step. If you have a project in mind, let's talk.