“Let’s rewrite it from scratch” is software’s version of “I’m starting a completely new life on Monday.”
The sentence is emotionally satisfying. Every bad decision will disappear. The architecture will be clean. Variables will have meaningful names. Nobody will connect directly to the production database this time.
The new system is imagined as a blank page.
The old one is described as a technological swamp: confusing code, outdated frameworks, strange database tables, and scheduled jobs understood only by somebody who has worked there since 2017.
The problem is that working software contains more than code. It contains years of business rules, user habits, integrations, exceptions, and emergency fixes that nobody had time to document.
Something that looks like a bug may be supporting the daily operations of three departments.
The old system may be ugly, but it is real
A new design looks logical because it has not yet encountered the full collection of illogical but legitimate demands produced by real users.
The old system has encountered them. The result may not be elegant, but the data exists, employees know how to use it, and the company expects it to work on Monday morning.
Martin Fowler used the Strangler Fig metaphor to describe a safer alternative to replacing an important system in one enormous cutover. The new system grows gradually around the existing one. Capabilities move piece by piece, and the old system is retired only when it is genuinely no longer needed.
The metaphor has lasted because it captures something that rewrite plans often ignore: replacement is not a construction project happening on empty land. The new system must grow while the old one continues doing real work.
The central idea is simple:
Do not change everything at once.
That principle is useful not only for legacy modernization, but also for designing new products.
A product can create value beside an existing system before it earns the right to replace anything. It can solve one painful step, expose information the current tool hides, or translate between two workflows that do not quite meet. This is less dramatic than declaring a platform revolution. It is also more likely to be useful next Tuesday.
Resist becoming the center of the user’s universe
New software often imagines itself as the future center of operations.
“Move all your work here.”
“Make us your single source of truth.”
“You can cancel your other tools.”
This sounds excellent in a pitch deck. To a user, it may mean:
Migrate your data, retrain the team, rebuild reports, replace integrations, and abandon years of familiar workflows. Our new product is still in beta, but the logo is extremely confident.
Most users are not searching for a new center. They want the center they already rely on to become less irritating.
That creates room for companion products, bridges, adapters, and extensions. They do not replace the existing system. They complete a missing workflow, add visibility, interpret data, or manage the boundary between tools.
A product does not need to conquer the user’s entire world. Sometimes there is a viable business in repairing one particularly unpleasant gap between two screens.
This approach also lowers the cost of trust. A replacement product asks the customer to believe two things at once: that the new software works, and that abandoning the old system will not destroy anything important. A companion product asks for a smaller leap. It can prove its value while the known system continues operating.
Integration is not a cable
Connecting two systems is often summarized as “we’ll call the API.” This belongs to the same family of innocent statements as “we only have a few boxes to move.”
Systems rarely share the same model. They may use the same word for different concepts. In one system, a customer is the account holder. In another, it is the recipient of a shipment. In one system, deletion means deletion. In another, it means adding a timestamp. A third system has a delete button but is legally prohibited from deleting anything.
Microsoft’s Anti-Corruption Layer pattern recommends placing a translation boundary between systems that do not share the same semantics. The purpose is to allow them to communicate without allowing the older or external model to dictate the design of the newer one.
A good integration layer does not merely rename fields.
It translates meaning.
That translation includes assumptions, defaults, timing, errors, identity, and ownership. It may need to turn one system’s event stream into another system’s state model. It may need to distinguish “unknown” from “empty,” even though both arrive as a blank string. It may need to preserve a legacy identifier that everybody dislikes because six external partners still depend on it.
This is why integration is sometimes not a technical detail at the edge of a product. It is the product.
The adapter may contain the most valuable understanding in the entire system: how the old world actually behaves and how the new one expects to behave.
Coexistence is not failure
Running old and new systems together is often treated as an awkward temporary phase. If the new system were truly good, the thinking goes, the old one could be switched off immediately.
In reality, coexistence is one of the healthiest ways to manage risk.
AWS’s branch-by-abstraction guidance describes how old and new implementations can operate behind a shared abstraction. Users or traffic can move gradually, results can be compared, and the team retains a path back when something fails.
It demands patience. It usually demands less medication than a plan beginning with, “We’ll perform the migration Friday night and everything will be ready on Monday.”
Incremental delivery has another advantage: it reveals whether the new system creates value before the entire program is finished.
If one completed part is already useful, there is evidence that the direction is sound.
If no part can deliver independent value, the grand launch two years later may not produce the expected miracle.
Coexistence also creates feedback that a clean-room rewrite cannot. Real users encounter real edge cases while the blast radius is still limited. Teams can compare old and new results, find missing behavior, and decide whether a strange legacy rule is accidental or essential.
That transitional architecture may eventually be deleted. This sometimes makes it look wasteful on a diagram. But temporary code that reduces migration risk can be cheaper than permanent code created by a failed replacement.
Sometimes replacement is necessary
None of this means old systems deserve permanent protection.
Some are impossible to secure. Some contain data that can no longer be trusted. Some have licensing costs, integration constraints, or operational risks that are no longer acceptable.
New requirements may fundamentally conflict with the old data model.
At some point, adding another layer around the legacy system may stop solving the problem and start expanding the archaeological site.
Replacement can be the correct outcome.
It should not automatically be the opening assumption.
The useful questions are:
- Where is the existing system still the correct authority?
- Which data and habits can users not afford to lose?
- Where can the new software begin delivering value independently?
- Which meanings need translation between the systems?
- Would the new product remain useful if the migration never finished?
- At what point does integration cost exceed replacement cost?
The question about unfinished migration is particularly revealing.
If the product remains useful even when the full transition never happens, you may have discovered something more durable than a migration utility.
Not every product needs to build an empire
Software products like to place themselves at the center: one panel, one platform, one source of truth.
Real life consists of a mixture of old, new, excellent, terrible, and “who approved this purchase?” systems.
A good product may not eliminate that mess. It can make the mess manageable.
Without replacing an existing system, it can expose missing information, interpret data, close a workflow gap, reduce migration risk, and avoid asking users to abandon every habit on the same day.
Sometimes the best product does not construct a new world.
It installs a reliable door in the world people already inhabit.
Frankly, that is what most users wanted from the platform in the first place. Not a new civilization. A door that opens.
Related reading
Sources and further reading
- Martin Fowler, Strangler Fig.
- Microsoft Azure Architecture Center, Anti-Corruption Layer pattern.
- AWS Prescriptive Guidance, Branch by abstraction pattern.
- AWS Prescriptive Guidance, Strangler fig pattern.