Full Scale named to the Inc. 5000 for the 5th time

Refactor vs Rewrite: When a Total Rewrite Is Actually the Cleaner Path

Matt Watson
Matt Watson
October 22, 202612 min read
Comparison chart with two sections: “Lean refactor” lists three points on improving structure; “Lean rewrite” lists two points on data model issues and gradual integration; both sections use bullet points.
In this article

The internet has mostly decided the refactor vs rewrite debate for you: refactor, never rewrite. The famous argument is that rewriting from scratch is the single worst mistake a software team can make, because you throw away years of tested, hard-won code. There’s real truth in that. But I think the “always refactor” rule is too simple, and after doing both more than once, I’ll make the case that the rewrite is sometimes the cleaner, faster path, as long as you go in with your eyes open.

I run Full Scale, and before that I built and modernized production systems at Stackify and VinSolutions. Here’s the honest tradeoff, from both sides.

Why refactoring always seems easier (and the handcuffs nobody mentions)

Refactoring sounds like the responsible choice. It’s a logical progression: clean things up, improve the structure, make the code better one safe step at a time. On paper it’s lower risk than a rewrite, and that’s exactly why most people default to it.

Here’s what that framing leaves out. The entire time you’re refactoring, you’re handcuffed to the system you’re trying to fix. You’re working around the existing production system, the existing users who can’t have their day interrupted, and very often a bad database model you’re stuck with because changing it would break everything sitting on top of it. Every change is a careful negotiation with code that’s already live. You spend an enormous amount of energy just not breaking things, and that constant caution is slow and exhausting in a way the “refactoring is safer” advice never admits.

Refactoring isn’t the easy option. It’s the careful option, and careful is expensive.

The case for the rewrite

A total rewrite, done in the right situation, removes those handcuffs. You’re not working around the old production system, the old users on the old paths, or the bad database model. You get a clean slate. You can build fast, build it in a completely new way, pick a different tech stack, and design the data model the way it should have been designed in the first place. For a team that knows what it’s building, that freedom translates into real speed.

That’s the part the “never rewrite” crowd underweights: refactoring’s safety is bought with a permanent tax of caution, and a rewrite can simply not pay that tax.

The catch: feature parity is always the struggle

The rewrite has one big trap, and it’s the reason the conventional wisdom exists. Getting a rewrite back to feature parity with the old legacy system is always a struggle. That old system does a hundred things, many of them undocumented, many of them edge cases someone fixed years ago for a reason nobody remembers. Matching all of it takes far longer than the demo that made the rewrite look easy.

And here’s the trap inside the trap: sometimes trying to support every legacy feature overcomplicates the new system and slows you right back down, until the clean rewrite has reaccumulated the same weight you were trying to escape.

The hidden upside: a rewrite is your chance to shed dead weight

The feature-parity problem is also where the biggest upside hides. Over time, systems build up an enormous number of features and functions that nobody actually uses. A refactor inherits all of it by default, because removing a feature from a live system is its own risky project. A rewrite lets you decide, deliberately, what not to bring across.

So a rewrite isn’t really “rebuild everything the old thing did.” Done well, it’s “rebuild what still matters and leave the dead weight and irrelevant technical debt behind.” That reframing changes the math, because you’re not actually chasing 100% parity. You’re chasing parity on the parts that earn their place.

The best of both: blend them with the Strangler pattern

The false choice is rewrite the whole thing or refactor the whole thing. The best modernizations I’ve done blended the two, and the technique has a name: the Strangler pattern, which I cover in depth in our guide to legacy application modernization.

Here’s a concrete example. At Stackify we had a .NET Framework application, and we really wanted to build new UI on .NET Core. So we didn’t rewrite the whole app, and we didn’t try to refactor the old UI in place either. We built a brand-new dashboard as a separate .NET Core UI microservice, and we blended it into the old application so well that a user moving back and forth between the two couldn’t tell the difference. That let us rewrite the dashboard module the way we actually wanted to build it, on the new stack, without touching the parts of the old app that were working fine.

That’s the move: rewrite the piece that needs it, on the stack you want, and blend it into the old system so the user never sees a seam. You get the rewrite’s clean slate on the part that matters and the refactor’s safety on the parts you leave alone. (We also wrote up the broader version of this in our .NET Framework to .NET migration guide.)

When a backend rebuild is justified, and when it is not

The rewrite question gets sharpest on the backend, so here is the decision as I actually make it: what does not justify a rebuild, what does, and the two ends of it from my own career.

The fake signs that trick teams into a rebuild

These are the reasons I hear most often, and none of them justify a rebuild on their own.

“It’s old.” Age isn’t a problem. I scaled a backend to a nine-figure exit on technology that was already unfashionable at the time. Old and boring and working beats new and exciting and unproven almost every time.

“It’s ugly.” Ugly code that works is an asset. A senior engineer’s discomfort with the previous team’s style is not a business case. Refactor the parts you touch.

“A new hire wants to.” A strong engineer who just joined will almost always want to rewrite what they don’t yet understand. That’s a signal they haven’t learned the system, not that the system is wrong. Give it three months.

“There’s a better framework now.” There’s always a better framework now. Migrating to it is a cost with no customer-facing payoff unless the current one is actually blocking you.

“We’ll move faster on a clean slate.” You won’t. You’ll move slower for a long time while you re-implement everything the old system did, including the parts nobody documented.

Building a development team?

See how Full Scale can help you hire senior engineers in days, not months.

If your reasons for a backend rebuild are on this list, you’re looking at a technical debt problem you can refactor your way out of, not a rebuild.

The real signs a backend rebuild is justified

Now the other side. These are the signs that a rebuild is the right call, and the pattern is the same in all of them: the architecture is blocking the business, not just annoying the engineers.

The architecture blocks what customers are paying for. When the thing your customers want next is impossible to build on the current design, not hard, impossible, that’s a real sign. If adding the feature would mean rebuilding half the backend anyway, the rebuild is on the table. Outgrown template storefronts are the classic case here, which is why so many custom e-commerce builds start life as exactly this kind of rebuild decision.

You’ve actually hit the scaling wall. Not “we might need to scale someday.” You’ve hit it, the cost of serving more load is climbing faster than the load itself, and you’ve already squeezed the obvious wins. Real scale problems justify real architecture changes. Imagined ones justify nothing. More on this in our piece on software scalability.

The stack is a genuine dead end. The runtime is unsupported, the framework is abandoned, the security patches stopped coming, and you can’t hire anyone who’ll touch it. That’s not aesthetics. That’s risk, and it compounds. The same judgment applies to an aging mobile codebase, which is the heart of what to do with a legacy Objective-C codebase.

Every change is a gamble. There are no tests, nobody fully understands the system, and each deploy has a real chance of taking production down. When the cost of changing the code safely exceeds the cost of rebuilding it with tests and structure, the math finally favors a rebuild.

When two or more of these are true at once, you’re not avoiding a rebuild anymore. You’re choosing the right one.

The story behind both: VinSolutions and Stackify

I’ll give you the two ends of this from my own career, because they’re the clearest version of the decision.

At VinSolutions, the number one CRM in automotive, we scaled to $35 million in annual recurring revenue on essentially one big database server, a maxed-out Dell, plus read replicas. Plenty of smart people would have looked at that architecture and called for a rebuild into something modern and distributed. We didn’t, because it wasn’t blocking the business. It was ugly and it was old and it worked, so we put our money into the product instead. A rebuild there would have been the most expensive mistake we could have made.

The part I left out is what scaling that database in place actually looked like. As we grew, one big SQL Server stopped being enough, and plenty of smart people would have called for a ground-up rebuild into something modern and distributed. We didn’t. We added read replicas to carry the read load, which was fragile and cost us plenty of bad nights, and we pulled the busiest tables out onto their own database servers to relieve the main one. None of it was elegant, and it kept a nine-figure company running while we spent our money on the product instead of a rewrite. Refactoring under load is uncomfortable and unglamorous, and it still beats the rewrite almost every time.

At Stackify, the opposite was true. We ran around 2,000 sharded SQL Server databases to ingest application logs and performance data at a volume no single database could hold. That was a real scaling wall, and the architecture had to change to match it. The rebuild was justified because the load was real, not theoretical.

The difference was never the age or the elegance of the code. It was whether the architecture was actually in the way.

How AI changes the decision

AI has dramatically changed how easy it is to do both. It lowers the cost of refactoring, because it can read and understand the old code and suggest safe restructurings. And it lowers the cost of a rewrite, because it can scaffold the new version and translate old logic into a new stack far faster than by hand. So AI doesn’t settle the debate; it makes both sides cheaper, which means you get to choose on the merits rather than on which one is less painful.

The honest limit is the same everywhere: AI-generated code carries real bug and security risk, so whichever path you pick, a senior engineer still owns the judgment and the review.

My honest lean

If I have my choice, I often reach for the rewrite, because you can just go fast and build it. When you’re stuck doing a refactor, you have to be so careful not to break existing things that the work crawls, and that’s genuinely hard to sustain. The rewrite, blended in well, lets a good team move at the speed it’s capable of.

I’ll also say where this goes wrong at scale, because it’s the same dynamic in a darker form. This is a big reason innovation dies in large companies. There can be decades of history baked into how things are done, and it’s enormously hard to get people to change it and rebuild that engine while the rocket is still flying through the air. The refactor handcuffs aren’t just technical there; they’re organizational. The teams that keep innovating are the ones that find a way to carve off a piece, rebuild it clean, and blend it back in, instead of telling themselves they can never stop and rewrite anything.

Refactor vs rewrite decision guide: lean refactor when the system is sound, lean rewrite when the data model or stack is the problem, and never big-bang it.

So which should you choose?

A rough rule, honestly stated:

  • Lean refactor when the system is fundamentally sound, the data model is fine, and you mostly need to clean up structure. You can’t justify the parity risk of a rewrite for a system that basically works.
  • Lean rewrite when the data model or the stack is the actual problem, when you can shed meaningful dead weight in the process, and especially when you can blend the new piece in with the Strangler pattern instead of attempting one big-bang replacement.
  • Either way, don’t big-bang it. The disaster case isn’t “rewrite,” it’s “rewrite everything and swap it all on one day.” Carve it down and blend it in. The broader set of ways these projects go wrong is in application modernization challenges.

Frequently asked questions

What’s the difference between refactoring and rewriting?

Refactoring improves the structure of existing code without changing what it does, one careful step at a time, while the system stays live. A rewrite builds a new version from scratch, often on a new stack, and replaces the old one. Refactoring is lower risk but slow and constrained; a rewrite is faster and cleaner but has to win back the old system’s feature parity.

Is it better to refactor or rewrite legacy code?

It depends on what’s actually broken. Refactor when the system is fundamentally sound and you just need to clean it up. Rewrite when the data model or tech stack is the real problem, when you can drop dead features in the process, and when you can blend the new version in incrementally rather than replacing everything at once. The riskiest option is a full big-bang rewrite with a single cutover.

Why is rewriting software considered risky?

Mostly because of feature parity. A legacy system does many things, including undocumented edge cases fixed years ago, and matching all of that takes far longer than the initial rewrite looks like it will. Trying to support every old feature can also reintroduce the same complexity you were escaping. The risk drops sharply when you rewrite incrementally and deliberately shed features nobody uses.

Can you refactor and rewrite at the same time?

Yes, and it’s often the best approach. Using the Strangler pattern, you rewrite the specific piece that needs it, on the stack you want, and blend it into the existing system so users don’t notice a seam, while leaving the parts that work alone. We did this at Stackify by building a new .NET Core dashboard as a separate UI microservice alongside the old .NET Framework app.

How has AI changed the refactor vs rewrite decision?

AI lowers the cost of both. It helps engineers understand and safely restructure old code for a refactor, and it scaffolds and translates logic for a rewrite. That means the choice is now made more on the merits than on which path hurts less, though AI-generated code still needs senior review for bugs and security.

The decision is a strategy, not a dogma

Refactor vs rewrite isn’t a rule you follow, it’s a strategy you choose for the situation in front of you. Refactoring is the careful path and carries a real, ongoing cost in caution. A rewrite is the fast path and carries a real risk in feature parity. The best teams stop treating it as either-or, carve off the piece that needs rebuilding, do that piece clean, and blend it back in so nothing breaks.

If you’d rather make that call with engineers who’ve done both, that’s what we do. Talk to us about your legacy system, and we’ll tell you honestly what to refactor, what to rewrite, and how to do it without taking the business down.

Ready to add senior engineers to your team?

Book a 15-minute call. Tell us your stack and where the gaps are, and we'll show you the engineers we'd put on your team.