Rebuild vs Refactor: How to Decide Whether to Rewrite Legacy Software
Most legacy software should be upgraded in place, not rewritten. Incremental refactoring keeps the business running and the data safe while the stack moves forward one tested step at a time. A rebuild wins when the data model is wrong for the business, the platform can't support the product's next stage, or the system is small enough to replace cheaply.
You have an application the business depends on. It's old. Every change takes longer than it should, the developers wince when they open it, and someone has finally said it out loud: "we should just rewrite it."
That sentence is one of the most expensive in software. Sometimes it's right. I've done a full rebuild when it was the correct call. I've also picked up a project where a rewrite had already been tried and abandoned, and the right answer was to upgrade everything underneath the running app.
Same question, opposite answers. Here's how I tell them apart, and what each choice costs in money, time and risk.
What is the difference between rebuilding and refactoring?
Refactoring changes how the code is built without changing what it does. Rebuilding replaces the system with a new one that is supposed to do the same job, plus more. "Rewrite" and "rebuild" mean the same thing here.
A refactor moves the code and its frameworks forward a piece at a time while the app keeps running. A rebuild replaces everything, often including the data model, and delivers its value on cutover day.
Two projects, two opposite answers
TowerSight: the rebuild was right
Ridgeline Engineering had a single-tenant internal tool for managing telecom infrastructure. It did the job, but it had hit a wall:
- Hard to extend. Every new feature meant fighting the architecture.
- Built for one organization. It could never be handed to customers.
- No self-service. Every new site, tower or asset had to go through Ridgeline staff.
- The data model didn't match the business. It tracked assets, but nothing tied them to the physical sites the team actually reasons about.
That last point decided it. You can upgrade a framework in place. You can't upgrade your way out of a data model that's wrong for the business, because every screen, report and permission sits on top of it. The multi-tenant SaaS Ridgeline needed next required a different foundation.
So I rebuilt it around the Site as the core entity, with multi-tenancy from day one, self-serve onboarding and five-tier role-based access. It went live in under four months with all of the legacy data migrated as part of the launch, not as a later phase. Their own summary: "A focused rebuild reached durable ground faster than patching would have."
Blazin Safety: the rewrite had already failed
Blazin Safety's inspection platform carried seventeen years of records on Laravel 6, PHP 7.2 and Vue 2, all long past end of life. There were no automated tests. An abandoned library sat directly in the login path. And a ground-up rebuild had already been attempted: it stalled at roughly 18% of scope and was retired.
The data couldn't move either: around 225,000 service records and roughly 975,000 audit-history rows forming the compliance record for safety equipment. Losing some of that isn't an inconvenience. It's a liability.
So I didn't rewrite it. I upgraded it in place across gated phases, one concern at a time, to Laravel 11, PHP 8.2 and Vue 3. Zero database schema changes, the audit trail intact, 103 automated tests where there were none, the abandoned login library replaced, and every phase revertible. The people who use it daily haven't had to change how they work. The detail is in the Blazin Safety case study.
The line from that project I'd put on a wall: "the real specification lives in the running application, not in any document."
Why do software rewrites fail so often?
Because the old system knows things nobody wrote down: years of bug fixes, edge cases and odd validation rules. Each one is a requirement, and rewrite teams usually discover them one at a time, after they've promised a date.
On Blazin, the first phase wasn't upgrade work at all. It was an inventory of every screen, field, validation rule and role gate: about 150 findings that became the written definition of "unchanged." Even misspelled column names stayed misspelled, because code depends on them. A rewrite needs that same work, usually skips it, and finds out at launch. I've written about how unrecorded knowledge turns into cost in Documentation Debt.
The rest of the reasons are about money and time:
- You pay twice. The old system still needs hosting, security patches and support while the new one is built. Two systems, one budget.
- The target moves. The business keeps asking for features. You either freeze the old system and make everyone wait, or build every feature twice.
- Value arrives at the end. Until cutover, a rewrite delivers nothing users can touch. If money or patience runs out partway, you've bought nothing you can use. That's exactly where Blazin's first attempt ended up.
- The last stretch is the hardest. Data migration, edge cases and cutover all land at the end. It's the same pattern I described in Why Your AI Project Is Stuck at 80%.
When is rebuilding the right call?
A rewrite wins when the problem is the foundation, not the code sitting on it. Rebuild when most of these are true:
- The data model is fundamentally wrong for the business. Not messy, wrong.
- The platform can't support the product's next stage. Single-tenant to multi-tenant, or internal tool to customer-facing product.
- The codebase is small, so there's little forgotten behaviour to miss.
- There's no live data risk, or the migration is simple enough to test fully and include in "done."
- You can name the business outcome the new system delivers that the old one can't. "Cleaner code" is not an outcome.
- One accountable owner and a tight scope. Rebuilds that grow a wish list don't finish.
If you can't point at the foundation as the problem, you don't have a rebuild case. You have an old app that needs maintenance.
When is incremental refactoring the better choice?
Upgrade in place when most of these are true:
- The running app is the spec. Nobody can fully describe what it does, which means nobody can fully rebuild it.
- There's a lot of live data, especially with audit, compliance or legal weight.
- The business can't pause. A cutover weekend that goes wrong costs real revenue.
- A previous rewrite has already stalled. The strongest signal there is: whatever stalled it is usually still there.
- The stack is old but the data model is basically right. Old frameworks can be upgraded. Wrong models can't.
- There are no tests. You need that safety net first anyway, whichever path you take.
What is the strangler fig pattern?
The strangler fig pattern, named by Martin Fowler after a vine that grows around a tree until it replaces it, means building new pieces around the old system and moving work to them one slice at a time. At every point, the system works.
In practice, incremental modernization takes one of two shapes:
| Approach | How it works | When it fits |
|---|---|---|
| Strangler fig | New modules run alongside the old app; features move across one at a time | You do need a new architecture, but can't afford a big-bang switch |
| Upgrade in place | Keep the app, move the runtime, framework and libraries forward a version at a time behind tests | The architecture is sound and the stack is just old (Blazin) |
What makes either work is the gate. On Blazin, every phase had to pass a written checklist: tests green, a live walkthrough on real data, parity and audit integrity confirmed, then a tagged build you can roll back to. Those walkthroughs caught real defects, like every date picker rendering blank after a build-tool change, before any user saw them.
How much does a rebuild cost compared with a refactor?
I spent eight years owning a P&L across development, support and hosting, so I look at this the way a finance person would. The sticker price matters less than the shape of the spend, and what you hold if the project stops halfway.
| Incremental refactor | Rebuild | |
|---|---|---|
| Money | Spread across phases; stop any time and keep what you paid for | Front-loaded, plus the full cost of running the old system until cutover |
| Time to first value | The first phase | Cutover day |
| Risk | Small, reversible steps | Concentrated at cutover, plus forgotten behaviour |
| Headcount | A small team alongside normal work | Often a separate team, or the old system starves |
| If it stops halfway | A better, still-working system | A partial system nobody uses |
A rebuild can still be the cheaper path. TowerSight shows that: patching a wrong foundation keeps costing money forever. But a rebuild has to earn that with a foundation problem, not a preference.
Signs a rewrite is ego, not need
Some rewrite proposals are really about the people proposing them. Watch for:
- The main argument is that the code is ugly or old. Ugly code that works is a maintenance task, not a business case.
- The pitch leads with the new framework. Excitement about a tool is not a reason to replace a working system.
- A new team or agency wants to start fresh before it has learned what the existing system does. If you've just inherited a codebase, use a technical due diligence checklist before anyone touches it.
- Data migration is "phase two." A rebuild that can't take the real data is a demo.
- The estimate is one round number. See how to evaluate a software development estimate before you sign it.
- A failed rewrite is followed by "this time will be different," with no explanation of what is different.
Get an independent answer before you commit
You make this decision once and pay for it for years. It deserves an opinion from someone who isn't going to be paid to do the rewrite.
This is exactly what a Technical Assessment covers: I read the code and the data model, talk to the people who use and maintain the system, and give you a written recommendation on whether to rebuild, upgrade in place or strangle it slice by slice, with the cost and risk of each.
FAQ
Should I rewrite my app or refactor it?
Refactor in most cases. Upgrading in place keeps the app running, protects your data and delivers value after every step. Rewrite only when the data model is fundamentally wrong for the business, the platform can't support the product you need next, or the codebase is small enough that replacing it carries little risk of losing behaviour.
Why do software rewrites fail?
Rewrites fail because the old system holds years of undocumented rules, edge cases and fixes that the new one has to rediscover. Meanwhile you pay to run both systems and nothing reaches users until cutover. If funding or patience runs out partway, you're left with an unfinished system nobody can use.
What is the strangler fig pattern?
The strangler fig pattern is a way to replace a legacy system gradually. You build new components alongside the old application and move features across one at a time, retiring each old piece once its replacement is proven. The system keeps working throughout, so there is never a risky all-at-once switchover.
How long does it take to modernize legacy software?
It depends on the system's size and how much live data it carries. A focused rebuild of a contained internal tool can ship in a few months. An in-place upgrade of a large, data-heavy application runs in gated phases over several months, with a working, revertible system after every phase.
Is it cheaper to rebuild or refactor legacy code?
Refactoring is usually cheaper and always lower risk, because cost is spread across phases and every phase delivers a working system. A rebuild front-loads cost and keeps the old system's running costs until cutover. Rebuilding can still be cheaper overall when the foundation is wrong, because patching a wrong data model never stops costing money.
What should I do if a previous rewrite already stalled?
Treat it as strong evidence for an incremental approach. Whatever stalled the first rewrite, usually undocumented behaviour, live data and a business that can't pause, is almost always still there. Start by inventorying what the running app actually does, add automated tests, then upgrade one concern at a time behind gates you can roll back.
Related Reading
-
Technical Due Diligence Checklist: What I Check Before You Buy, Invest or Sign: What to check in a codebase before you commit money to it.
-
Build vs Buy Software: How to Decide, With the Real Costs: The other big platform decision, with the multi-year costs both ways.
-
Why Your AI Project Is Stuck at 80% (And How to Actually Ship It): Why the last stretch of a project is where schedules break, and how to plan for it.
If someone has told you the only way forward is to rewrite, get a second opinion before you fund it. Get in touch if you want an honest read on what your system actually needs.
Comments Coming Soon!
We're building a space for builders to share insights.
Start a Conversation →For now, reach out directly with your thoughts!