Replatform, Modernize, or Rebuild: Telling the Three Apart
A retail team says "we need to replatform," and 20 minutes later the meeting is about vendors. That is the wrong order. The word arrives sounding like a diagnosis when it is actually a prescription, and nobody has said out loud what hurts.
Replatform, modernize, and rebuild get used as synonyms in planning decks. They are not synonyms. Each one changes a different object, and picking the wrong one is how a 6-month programme turns into a 2-year one.
The three words name three different objects
Replatform changes where the code runs. The application logic survives mostly intact while the runtime, the managed services, and the operational model change underneath it. AWS calls this "lift, tinker, and shift" in its 7 Rs migration strategies, and that phrasing is refreshingly honest about scope: you introduce some optimization, not a redesign.
Modernize changes what the code looks like from the inside. Same business rules, same domain concepts, different structure. Modules with boundaries, tests that run, dependencies that resolve, a build that finishes before you lose the thread.
Rebuild changes what the code believes about the business. The domain model is wrong, usually because it encodes a version of the company that stopped being true 5 or 10 years ago.
Most retail teams reach for the first word when they need the second. The platform gets blamed because it has a vendor name and a renewal date. The codebase has neither, so it escapes the conversation.
The diagnostic question
There is one question that separates the three, and it takes an afternoon to answer rather than a discovery phase. When a competent engineer who already knows the domain sits down to make a small change, what stops them?
If the answer is provisioning, scaling cost, a database version stuck 2 majors behind, or the fact that nobody can get a clean staging environment, the constraint is the runtime. That is a replatform.
If the answer is that nobody knows what a class does, that there are no tests around the path being touched, or that a 1-line rule change spans 14 files across 3 layers, the constraint is the codebase. Moving that codebase to a new platform relocates the problem and adds a migration on top of it.
If the answer is "the model has no concept of that," the constraint is the domain. An order record with a single fulfilment location cannot represent ship-from-store however clean the surrounding code is. That is a rebuild, though usually of one bounded context rather than the whole estate.
The decision table
| Dimension | Replatform | Modernize | Rebuild |
|---|---|---|---|
| What changes | Runtime, infrastructure, operational model | Internal structure, tests, build, dependencies | Domain model, data shape, business rules |
| Primary symptom | Deploys are slow or fragile, infra cost is climbing, a runtime is past end of life | Change is risky, onboarding takes months, regressions arrive with every release | The business asks for something the schema cannot express |
| Business rules | Preserved as-is, deliberately | Preserved, and finally documented by tests | Renegotiated with the business |
| Data migration | Usually a lift, sometimes an engine swap | None | Mandatory, and the hardest part of the project |
| Reversibility | High. Roll back to the old environment | Medium. Revert per module | Low. Once the new model is the system of record, back-out is a project of its own |
| Typical failure | The same slow code in a more expensive place | Scope creep into an unbudgeted rebuild | Requirements archaeology stalls it for 2 quarters |
| Wins when | An infrastructure contract, licence, or support window is forcing a date | The code still encodes correct business rules and the team is the bottleneck | The model is wrong, not the code |
Replatform: the boring option that is often correct
A replatform earns its keep when the forcing function is external. A support window closes, a licence renews, a data residency rule moves, a runtime stops receiving security patches. Those dates do not negotiate, and a replatform is the cheapest way to satisfy them.
The trap is expecting behaviour change. Moving a monolith into containers does not make it faster to change, and moving a database into a managed service does not fix a query that scans a table because there is no index. You get an operational improvement and an unchanged development experience.
AWS is blunt about the sequencing. Its guidance recommends rehosting, relocating, or replatforming first and modernizing after the migration completes, because refactoring during a large migration is "the most complex and costly of the migration strategies." Doing both at once means every incident has 2 candidate causes.
Modernize: where most retail codebases actually sit
The typical enterprise retail estate is not badly hosted. It is badly layered. A decade of seasonal pressure has left template logic calling service logic calling a stored procedure that also writes an audit row.
Modernization work is unglamorous and mostly mechanical. Carve a seam, put a test around the behaviour you are about to move, move it, delete the old path. Repeat until the module has a boundary you could draw on a whiteboard.
One multi-generation modernization we ran moved a storefront off JSP templates fronting a legacy CMS, first to React and then to Next.js with micro-frontends, while backend services moved to Spring Boot one service at a time. The first rewrite took the PageSpeed score from 60 to 90 or better. No phase of it was a big-bang cutover, and the legacy stack kept serving the surfaces that had not moved yet.
That last detail is the whole method. If a modernization plan has a single cutover weekend in it, it is a rebuild wearing a modernization label.
Rebuild: reserve it for a wrong model, not for ugly code
Ugly code is a modernization problem. A wrong model is a rebuild problem, and the difference shows up in the requirements conversation rather than the code review.
Signals that the model is genuinely wrong: the same business event is represented 3 incompatible ways in 3 tables, pricing rules live in application code because the schema has nowhere to put them, or the operations team maintains a spreadsheet that the system cannot replace. In those cases refactoring produces a tidier version of the wrong answer.
Rebuilds fail for a predictable reason. The old system encodes 10 years of undocumented edge cases, and the new one ships without them. Budget the archaeology explicitly, and treat every "surely nobody uses that" as a claim requiring evidence from production logs.
Instrument the decision before you argue about it
Most replatform-versus-modernize arguments are conducted entirely on anecdote. Delivery metrics settle them faster than opinions do. The DORA metrics now cover 5 measures rather than the original 4: change lead time, deployment frequency, failed deployment recovery time, change fail rate, and deployment rework rate.
Read them as a pair of signals. Long lead time with a healthy change fail rate points at the codebase and the review path, because the work is slow but correct. High change fail rate and slow recovery point at the runtime and the release mechanics.
Collect 8 to 12 weeks of this before committing a budget. It costs almost nothing and it converts a stakeholder argument into a reading of data everyone can see.
The coexistence tax nobody budgets
Every honest modernization runs two systems at once for a while. The Strangler Fig pattern is the standard way to do it: a facade sits in front of both systems and routes a growing share of traffic to the new one until the old one has nothing left to serve.
The pattern is well documented, including where it does not apply. Microsoft's guidance lists 4 disqualifiers: you cannot intercept requests to the back end, you cannot access the legacy source code, the system is small enough to replace outright, or you need the original decommissioned quickly.
The second disqualifier catches more retail teams than expected. Plenty of estates include a vendor component nobody can modify, and the facade approach quietly assumes you can turn off migrated features inside the legacy system.
While both systems are live they have to talk, and that traffic is where legacy semantics leak into new code. The anti-corruption layer exists to stop that: a translation boundary where requests are mapped between the two data models so neither has to adopt the other's vocabulary.
Keep it to translation. Once business rules or orchestration move into that layer, it becomes a third system that outlives the migration it was built to support.
The cost shapes are different, not just the totals
Finance usually asks for one number per option. The more useful comparison is the shape of the spend over time, because that is what determines whether the programme survives a bad quarter.
A replatform is front-loaded and then stops. Most of the cost lands in a migration window, after which the run rate changes and the engineering team goes back to feature work.
Modernization is flat and long. It is a fixed share of engineering capacity applied continuously, which makes it easy to defund quietly and hard to restart, because half-migrated seams rot faster than untouched ones.
A rebuild is a step function with a cliff. Spend accumulates with no user-visible return until the new system takes over a bounded context, and every month of delay adds to the double-run cost of keeping the old system alive.
Match the shape to the organisation. A team that cannot protect a standing capacity allocation should not choose modernization, whatever the diagnosis says, because the plan requires a commitment the company has already shown it will not keep.
What the first 3 weeks look like
On a modernization engagement the opening move is not a proposal. It is a read of the delivery metrics, a dependency map of the top 20 change-frequency files, and a list of every place the domain model and the business vocabulary disagree.
Those 3 artefacts almost always decide the question on their own. Where they disagree, the disagreement itself is the finding worth presenting.
The recommendation that follows is usually mixed. Replatform the 2 services with an expiring runtime, modernize the checkout path because that is where the change traffic is, rebuild nothing this year. Mixed answers are harder to sell than a single word, and they are right more often.
FAQ
Can we replatform and modernize at the same time?
Technically yes, and it is the most common way these programmes go over budget. Every production incident then has 2 plausible causes, and the team spends its debugging time proving which. Sequence them unless a hard external date makes that impossible.
How do we know the domain model is wrong rather than just messy?
Ask the operations team to describe a routine exception, then find where the system represents it. If the answer is a free-text note field, a spreadsheet, or a status code being used for 2 unrelated meanings, the model is missing a concept. Messy code represents the right things badly; a wrong model does not represent them at all.
Does a headless or composable target platform change the answer?
Not the diagnosis. It changes the shape of the replatform option, because a composable target moves more of the operational surface onto your team. Diagnose first, choose the target second.
What if the vendor contract expires before modernization can finish?
Then the replatform is scheduled for you, and the modernization becomes a follow-on programme with its own budget. Trying to compress both into the contract window is how teams end up with a new platform running the same untestable code.