Skip to content
  1. Home
  2. Articles
  3. Rewrite the Whole System or Gradually Refactor? A Decision Framework for Legacy Systems
120
Enterprise

Rewrite the Whole System or Gradually Refactor? A Decision Framework for Legacy Systems

Rewrite the Whole System or Gradually Refactor? A Decision Framework for Legacy Systems

A decision framework for executives and IT leads: should an aging system be rewritten from scratch, refactored gradually, or replaced piece by piece with a strangler approach — with the signals that decide and the cost of choosing wrong at data migration and cutover.

When a budget request to replace an aging system goes up for approval, the board usually asks when the new system will be ready and how much it will cost. The question that falls through the cracks more often is whether the organization should rewrite the whole thing from scratch or gradually refactor what already exists. These two paths differ in the risk and cost that surface in year two, not just the price on the first quote. Choosing the wrong path here is often more expensive than picking the wrong technology, because it decides what the organization will carry for years. This article is written from the perspective of the people who have to live with the system after handover, not the ones who close the project and walk away. Our aim is to give you a frame that answers one question clearly: should the system in front of you be rewritten or refactored, and why — so you can defend that decision to a committee.

What rewrite and refactor actually mean

The two words get used interchangeably until their meaning blurs. Rewrite means building the system anew from zero, discarding the old code and creating something that does the same job or better. Refactor means restructuring the internals of the existing code so it is cleaner and easier to maintain, while the behavior users see stays the same. The most important difference is that refactor keeps the business logic that has accumulated over time, while rewrite starts counting from one.

This confusion is not just about vocabulary. When a team says "let's just refactor" but actually plans to tear down the architecture and swap the entire stack, that is a rewrite wearing a less risky-sounding name. The board approves a budget believing it is a small adjustment, and what they get is a full rewrite that runs for a year. Using the right word from the start prevents a misunderstanding that turns into an expectations problem later.

In practice, the choice is not binary. AWS frames migration and modernization as 7 R's — rehost (lift and shift with no changes), replatform (lift and reshape with some optimization), repurchase (move to a SaaS or off-the-shelf product), retain (keep it where it is for now), retire (decommission what no longer has value), relocate (move without changing the architecture), and refactor or re-architect (redesign the architecture). Per AWS Prescriptive Guidance, refactor is the most complex and costly of the set, and they recommend doing it after a migration rather than during one. The value of the frame is that it forces you to ask, before jumping to a rewrite, whether a lower-risk middle path exists.

Why "rewrite everything" is tempting, and the trap it hides

A rewrite always sounds cleaner. The team gets a blank page, picks the stack it wants, and no longer has to tolerate unreadable code left by someone who has already resigned. That feeling is real and understandable, but it hides several invisible costs.

Joel Spolsky argued back in 2000, in Things You Should Never Do, Part I, that deciding to rewrite from scratch is the single worst strategic mistake a software company can make. His reasoning: the messy-looking old code carries years of accumulated bug fixes, and each odd-looking line is usually the trace of a real problem that was already solved. He cites Netscape, which decided to rewrite its browser entirely and lost years while competitors moved ahead. Rewriting throws that knowledge away and risks repeating old mistakes while adding new bugs on top.

Spolsky's emphasis is that code is harder to read than to write, so programmers routinely underestimate an existing system — they see the mess and assume a rewrite will be faster, when part of that mess is embedded knowledge.

The other trap is the second-system effect, which Fred Brooks described in The Mythical Man-Month back in 1975. A team freshly released from the constraints of the old system tends to cram every feature it ever wanted into the second one, bloating what should be simple until it becomes hard to build and slower than it should be. This is why so many rewrite projects run many times longer than their original estimate.

A cost that rarely gets discussed is maintaining two systems at once. While the team pours its time into the rewrite, the old system still has to run and still has to be patched. New features the business asks for wait, because everyone is building something new that does not work yet. The gap between the day the rewrite starts and the day the new thing is usable is the period when the organization pays for maintenance twice while getting zero new value — and when competitors can pull ahead.

Signs it should be a refactor, not a rewrite

If the old system still works correctly and the problem is that the code is hard to read and slow to change — not that it behaves wrong — that is a signal for refactor. Spolsky points out that architecture problems, performance issues, and code ugliness can all be fixed by gradual adjustment, without throwing away what works.

Other signals to keep the existing system include business logic that is complex and battle-tested over years, to the point where no one is confident a rewrite would reproduce it completely. The more that logic ties into organization-specific rules or legal requirements, the higher the risk a rewrite drops something. Another signal is that the team who understands the system is still around and can still maintain it, and the system has not hit a technology dead end.

In these cases, technical debt is something you manage in installments, not a reason to tear everything down. A workable approach is to scope each round of changes small enough to verify. Before touching any section of code, write tests that cover its current behavior first, so you can be sure the result stays the same after the change. Having tests as a safety net makes refactoring far safer and turns a scary task into a repeatable one. Refactoring section by section while adding test coverage for what you touch usually pays back faster and carries less risk, because the system keeps delivering value the whole way — no waiting for a big launch day.

Signs a rewrite or re-architect is genuinely worth it

There are situations where keeping the old system costs more than starting over. AWS places the refactor or re-architect case exactly here: when the old system is a mainframe or monolith that can no longer meet business demand, or is too expensive to keep maintaining. Another clear case is when no one knows how to maintain the system or the source code can no longer be found, and when the system is nearly impossible to test — test coverage so low that every change is a guess.

A case Thai organizations hit often but rarely count is security and PDPA. If the old system was designed before personal-data requirements existed, and its architecture does not allow the level of data separation or access control now required, patching the surface may not be enough to pass a review. In cases like this, re-architecting part of the system becomes a necessity, not a choice.

The criterion that actually decides is comparing two cost figures: the annual cost of maintaining and extending the old system, versus the cost of building new plus the risk of the transition. The cost of maintaining the old system is not just team salary — it includes the opportunity cost of features you cannot build because the old architecture will not support them, and the risk from a stack whose vendor has stopped shipping security patches. If the first figure is climbing every year on a clear trend, and the old stack is reaching end of support, the case for a rewrite starts to hold up. But it has to be a number estimated from your own system, not a feeling that new must be better.

The overlooked middle path: replacing piece by piece with strangler

When a rewrite is too risky and a refactor is not enough, there is a third way. Martin Fowler calls it the Strangler Fig Application. The idea is to build the new system alongside the old one, then gradually move behavior across piece by piece, until one day the old system is fully replaced and can be decommissioned. The name comes from the strangler fig tree, which grows around its host until it eventually replaces it.

Fowler explains that the heart of the approach is finding the breakpoints, or seams, where the system can be cut into pieces, then choosing to deliver one piece at a time — starting with the ones that show clear results at low risk. There is usually a middle layer that decides which requests go to the old system and which go to the new part already migrated. That layer is what keeps users from feeling the change happening behind the scenes.

The advantage is that risk is spread into small chunks. Each piece that is migrated delivers value to users immediately, and if a piece has a problem, the impact is confined to that piece rather than the whole system at once, as it would be in a big-bang. The organization gets to learn and adjust its way of working alongside the real migration. Fowler notes the price you pay is accepting a transitional architecture while the old and new run together, which adds complexity during the change — and it takes discipline to actually decommission the old parts once migrated, rather than leaving them running in parallel forever.

The downside has to be said plainly: this approach is slower at the start, and it carries the cost of the layer that bridges old and new. If your case is a small internal system with a few dozen users, no personal data, and one that can pause without hurting the business, the effort of that middle layer may not pay off. In that case, the straightforward and simpler route is the better one, because complexity no one ends up using does not vanish — it becomes a cost the team maintains every month.

The cost of choosing wrong that is not in the quote

The cost that blows up a system-replacement project is usually not the code. It is the data migration and the cutover to the new system. Microsoft's Cloud Adoption Framework splits migration into two methods: one with downtime, which is simpler and faster because it needs no real-time data sync, and one with near-zero downtime, which is necessary for systems with external users or strict SLAs but is more complex to set up and requires more testing. Getting this choice wrong hits both risk and cost directly.

Data migration carries its own risk that gets underestimated. Data accumulated over years in an old system tends to be inconsistent — duplicates, fields used for purposes other than their original one. Moving it into a new structure is not a copy; it has to be transformed and verified as still correct after the move. A copy on its own is not enough. A way to lower the risk is to run in parallel, letting the old and new systems operate together for a period, then comparing the results of both before cutting over from the old one for real.

The thing teams most often leave out of the plan is rollback.

Microsoft recommends defining in advance what counts as a failed deployment — a failed health check, degraded performance, an error rate over a threshold — and preparing steps to roll back to a known-good state that have actually been tested. Having clear numeric criteria means the decision to roll back in the middle of the night does not wait on a debate. Another hard-to-estimate cost is the knowledge lost at cutover: the person who understood why a particular set of data was structured a certain way may no longer be there when the move happens. A workable recommendation is to migrate the easy, non-critical systems first, and do non-production before production, so the team rehearses the full process before touching the real thing.

The last cost that tends to surface after handover is rights and documentation. If the new system is built by an external developer but the organization does not hold the rights to the cloud account and has no architecture documentation, maintenance in year two stalls immediately. This should be written into the TOR from the first round, not chased down afterward — and once the new system is live, having an incident plan for outages and a maintenance contract with a clear scope is what keeps the investment in replacing the system from going to waste over the long run.

How to decide in practice

Before choosing a path, answer this set of questions with data from your own system. Does the old system actually behave wrong, or is it just hard to maintain? If it is only hard to maintain, a refactor is usually enough. How much is the accumulated business logic worth? If it is high and complex, a rewrite risks dropping something. Is there still someone who understands and can maintain the system? If no one, that pushes toward rewrite or re-architect. Does the system have security or PDPA requirements its current architecture cannot support? If so, re-architecting part of it may be unavoidable.

The next questions are about cost and continuity. Is the annual maintenance cost climbing on a clear trend? Is the old stack still supported? And if you choose a rewrite, can the organization handle the period where the old and new systems must run in parallel, both in budget and in people? When the answers lean toward keeping the existing system, start with a refactor. When they lean toward needing an architecture change but you fear a large lump of risk, consider strangler as a way to reduce it. A full big-bang rewrite should be the last resort, when no other path is genuinely left.

These questions are best answered with someone who has lived with a system after handover, not only someone who would build it, because the two perspectives account for cost differently. The builder looks at the delivery date; the maintainer looks years out, to the third year when problems tend to surface all at once.

Summary

If there is one thing to take from this article, remember that the right question is not "is the new thing better?" but "which is higher — the cost and risk of keeping the old system, or of changing it, measured against our own system?" That answer points to whether you should refactor gradually, replace piece by piece with strangler, or rewrite. And almost every time, the wrong choice surfaces at the data migration and cutover, not in the coding.

If your organization is weighing this and would like someone to review the cost assumptions before deciding, reach us at 088-983-9386 or [email protected]. We are happy to talk through the problem first, with no complete documents required, and if the conversation shows it is not yet time to replace the system, we will say so plainly — because starting a project at the wrong moment costs more than waiting another quarter.

FAQ: Frequently Asked Questions about This Article

A collection of questions and answers to help you better understand the content of this article.

Tags:

legacy systemrewrite vs refactorrefactortechnical debtstrangler figbig bang migration
Share:

Other Articles

Stay tuned for upcoming articles!