You don't lift-and-shift your way to cloud-native.
Every previous stage taught you how to build in the cloud. This one is about everything that already exists somewhere else — and the uncomfortable fact that moving it and improving it are two different projects that people constantly confuse for one.
Three ideas hold this stage together. First: the R's are a spectrum of effort versus payoff, and you choose one per application, never per portfolio. A realistic enterprise migration plan has five different R's in it. Second: incremental beats big-bang. The villain of this stage is the freeze-everything-and-cut-over-once rewrite, and its lazy twin — lift-and-shift-then-stop, which relocates your technical debt to a more expensive address and calls it transformation. Third: assess before you move. Dependencies, data gravity, and cutover risk decide the sequence. A migration is a wave plan, not an event.
Stages 2–8 are the destination. Identity, network, compute, storage, application patterns, reliability, governance and cost — the landing zone you learned to build is the thing you migrate into. C9 never re-teaches the target. It teaches how to get things there without breaking them, and how to reshape them once they arrive.
diagram
The 7 R's, plotted honestly
Most diagrams of the R's are a list. A list hides the only thing that matters: each R buys you a different amount of cloud benefit for a different amount of effort, and the curve is not linear. Click any rung to see what it means, what it costs you, and a workload that belongs there.
Select a rung above to read its definition, cost profile, and the trap that comes with it.
Gartner published 5 R's around 2010–2011 (rehost, refactor, revise, rebuild, replace). Stephen Orban condensed and renamed them into 6 Strategies for Migrating Applications to the Cloud at AWS in 2016 — the "6 R's" the patterns catalog teaches. AWS later added a seventh, relocate, after VMware Cloud on AWS launched, to cover hypervisor-level moves that fit none of the other six. AWS Prescriptive Guidance documents seven today. Some AWS mainframe material now references an "8 R" set. Don't memorise the number. Memorise the axis: how much do you change, and what do you get for it.
be able to do
End state for this stage
- 01Pick the right R for a given application, and defend the choice against someone who wants to refactor everything and someone who wants to rehost everything.
- 02Take a portfolio, map its dependencies, and sequence it into waves that respect coupling and data gravity — including knowing which apps must move in the same wave.
- 03Apply the strangler-fig pattern to a monolith so that there is a working system and a cheap rollback at every single step.
- 04Do the data-gravity arithmetic on paper: how long a transfer takes on a given link, what it costs in egress, and the crossover where a physical appliance beats the network.
- 05Compare AWS, Azure and GCP migration tooling and hybrid platforms, and know which product names in the materials are already dead.
- 06Explain precisely why your own stateless, externalised-state model-serving stack is a genuinely easy migration — and where its real gravity actually sits.
sessions build
Three sessions, one arc
S1 gives you the decision framework with no products in it. S2 gives you the one modernization pattern that actually works at scale, also with no products in it. Only S3 introduces tooling — deliberately last, because a tool cannot rescue a bad sequencing decision.
corrections
Before we start: your reading list was wrong in three places
Three of your assumptions need correcting, and one of them changes the plan for S3.
Cloud_Patterns.pdf
the cloud application patterns catalog, a cloud patterns catalog . §9 is "Application Migration and Modernization." §10 is "Strangling Monoliths." Exactly as you had it. This material is the spine for S1 and S2 and it earns that role.
AWS notes does have a migration section
AWS guidance's guide has §26 "Migration Strategies and Tools" and §27 "Modernizing Existing Architectures." §26 covers a six-phase migration process, the 6 R's, Migration Hub, Application Discovery Service and DMS. So we do use the source for S3 — but see correction 3, because it is badly out of date on tooling.
GCP §14 is not migration
In GCP guidance's a leading GCP architecture reference, §14 is "Google Cloud Operations Suite" and §15 is "Agile, DevOps, SRE, SDLC…". There is no dedicated migration or hybrid section at all. The material is scattered: hybrid-cloud types in §1, Storage Transfer Service and Transfer Appliance in §3, Cloud VPN and Cloud Interconnect in §4, and the Dress4Win migration case study in §15. Anthos gets two passing mentions and no treatment. The material promises hybrid offerings "later in this material" and never delivers a section on them.
Azure.pdf confirmed — and all three are stale on tooling
Mastering Azure (vendor guidance et al.) has 16 section, none on migration; closest are §3–4 networking and §16 governance. As you expected, Azure Migrate / DMS / Arc come from docs. More importantly: the AWS source teaches Server Migration Service and CloudEndure as the primary rehost tools. Both are retired. It never mentions MGN. Full staleness audit is in S3.
Because no source has current migration tooling, every product name, capability and price in Session 3 is grounded in vendor documentation and news from the last six months, not the materials. Each table flags what the materials say versus what is true now, with links. Two of the changes landed within the last two weeks and one within the last day.
you'll need
Terms defined once, used throughout
Every one of these is defined properly at first use inside its session. This is the index, not the teaching.
- Landing zone
- The pre-built, governed destination from C1 — org hierarchy, accounts/subscriptions/projects, guardrails, networking, identity — ready before the first workload arrives. Migrating into an ungoverned account is how you get 400 untagged VMs.
- Data gravity
- Large datasets are slow and expensive to move, so compute tends to migrate toward the data rather than the reverse. Coined by the data-gravity essay in 2010. It is the single biggest constraint on wave sequencing.
- Wave / move group
- A batch of applications migrated together in one window because they share dependencies, data, or a cutover. The unit of planning in every real migration.
- Cutover
- The moment production traffic starts hitting the new target and stops hitting the old source. Everything before it is reversible; everything after it costs something to undo.
- Rehost
- Move it, change nothing. Also "lift and shift."
- Relocate
- Move the hypervisor estate wholesale — no OS change, no app change, no re-IP.
- Replatform
- Move it and swap components for managed services without touching application code. Also "lift, tinker and shift."
- Repurchase
- Stop running it; buy the SaaS equivalent.
- Refactor
- Change the application's architecture to use the cloud properly. The only R that fixes technical debt.
- Retain / Retire
- Decide not to move it, either because you can't (retain) or because nobody is using it (retire).
- Strangler fig
- Grow a new system around the old one, redirect one slice of traffic at a time, and remove the old code as each slice goes live. Named in the refactoring literature after the strangler vine.
- Anti-corruption layer
- A translation boundary that stops the legacy system's data shapes and vocabulary from leaking into the new service's domain model. Abbreviated ACL — nothing to do with access control lists.
- Dual-write
- A transition period where writes go to both the old and new data stores so either can serve as source of truth.
- Backfill
- Copying the historical rows the dual-write never saw, so the new store has full history and not just what happened since Tuesday.
- Hybrid
- On-premises and cloud running as one connected estate. Distinct from multi-cloud, which is two or more public clouds.
session exists
Two hundred applications, one team, and a data centre lease that expires in fourteen months
Nobody's migration fails because they picked the wrong instance type. Migrations fail because somebody treated a portfolio as if it were one thing.
Here is the actual situation, every time. There is a list of applications. Some are load-bearing and some are ghosts. Nobody has a complete dependency map, because the map lives in three people's heads and one of them left. There is a deadline that comes from a lease, a licence renewal, or an acquisition — not from engineering. And there is enormous pressure to answer one question: "so, how long will it take?"
The two failure modes are symmetrical. The refactor-everything plan is architecturally correct and finishes after the deadline, if ever. The lift-and-shift-everything plan finishes on time and delivers a data centre you now rent by the hour, with the same technical debt and a worse bill. Both come from the same mistake: applying one strategy to a whole portfolio.
This session is concept-only. There is no product called "wave planning" you can go and buy — though as of this year Azure Migrate has shipped a preview feature with exactly that name, which is a nice sign that the vendors have finally caught up with the practice. The one genuine three-cloud mapping here is the adoption frameworks, and it's a short table.
▸ the seven R's
The seven R's, one at a time
You are moving house, and every object in the house gets its own decision. The sofa you like goes on the van as-is (rehost). The old fridge stays behind because the new kitchen has one built in (repurchase). The boxes of paperwork from 2009 go in the skip (retire). The piano stays because the new place has no ground-floor room and moving it costs more than it's worth (retain). The flat-pack shelving gets rebuilt to fit the new alcove (replatform). And the kitchen itself — the thing you actually use every day, that has annoyed you for a decade — gets redesigned from scratch, which is why you do exactly one of those (refactor).
Nobody sensibly says "I will apply the same treatment to every object in my house."
Mechanically, the R's differ along three independent axes that the patterns catalog separates cleanly, and which are worth holding apart in your head:
- Platform — where it runs: traditional IT or cloud. Changing this is migration.
- Architecture — how it's structured: unstructured monolith, modular monolith, or distributed. Changing this is modernization.
- Packaging — how it's deployed: bare metal, virtual server, or container. Changing this is also modernization, and it's independent of architecture — you can containerise a big ball of mud, and people do.
These vary independently, which is the whole reason the R's exist as separate labels. Rehost changes platform only. Replatform changes platform and packaging. Refactor changes architecture, and usually all three.
Expand each R below for its definition, what it actually costs, and the specific trap that comes with it.
Retire Turn it off. Nobody will notice.
What it is: the application runs, but nobody meaningfully uses it. Shut it down instead of moving it.
Cost: near zero to execute, but real effort to prove — you need usage telemetry, a communication window, and an archived copy of the data for whatever your retention policy says.
Why it goes first: AWS's own guidance recommends retiring before waves begin, because every retirement shrinks the portfolio, removes a node from the dependency graph, and improves the business case. In large estates a meaningful fraction of applications turn out to be retireable. It is the cheapest win in the entire programme.
The trap: "nobody uses it" is a claim, not a fact. The classic disaster is retiring a service that one nightly batch job calls once a month. Turn it off with a dark period first — block traffic, keep it running, wait a full business cycle (usually a quarter, to catch month-end and quarter-end), then delete.
Retain Leave it where it is. On purpose.
What it is: a deliberate, documented decision not to move — with a review date.
Legitimate reasons: data sovereignty or residency rules; a hardware dependency the cloud can't reproduce (an HSM, a serial-attached instrument, a dongle); a vendor licence that doesn't permit cloud hosting; latency to a physical process; or an application so fragile that the risk of touching it outweighs any benefit.
Cost: zero to execute, but it is not free — you keep the data centre, the hardware refresh cycle, and the operational skills for it. A retain decision on one application can keep an entire facility open, which is why one retained app can be more expensive than migrating fifty.
The trap: "retain" quietly becoming "we forgot." A retain decision without a review date is not a decision, it's an omission. Also: check whether retaining it forces hybrid connectivity — a private link back to the data centre — because that cost belongs on the retain decision's ledger, not on the network team's.
Relocate Move the hypervisor, not the machines.
What it is: hypervisor-level lift and shift. Your vSphere estate moves onto a managed VMware-compatible service in the cloud. No OS change, no application change, no re-IP, and your team keeps using vCenter and its existing runbooks and automation.
Cost: the lowest per-VM effort of any R that actually moves something, but it carries a licensing and capacity commitment, and the hosts are chunky — you buy hosts, not instances, so it only makes economic sense above a certain estate size.
Why it's the seventh R: it genuinely didn't fit anywhere. It isn't rehost (rehost converts each machine into a native cloud instance); it isn't replatform (nothing is swapped for a managed service). AWS added it after VMware Cloud on AWS launched.
The trap: relocate delivers speed and continuity, and almost no cloud benefit. It is the most comfortable R and therefore the easiest one to stop at. Treat it as a staging position with a follow-on plan, not a destination.
Rehost Lift and shift. The workhorse.
What it is: recreate an equivalent compute environment in the cloud and put the application on it, changing as little as possible. The patterns catalog gives four concrete steps that are worth memorising because they are the whole job: duplicate the compute environment (same OS family and version); duplicate the network environment (the VPC/VNet stands in for the old LAN — many clouds even let you bring your own IP ranges); connect to any on-prem resources that can't move yet (SSO, monitoring, systems of record) over VPN or a dedicated link; and resolve data dependencies — either move the database or reach back to it.
Cost: lowest risk, fastest schedule, and by far the most common choice at scale. AWS field guidance notes that in large legacy migrations organisations often rehost 70% or more of applications, because speed and low disruption outweigh optimisation early on.
The trap — and this is the important one: rehosting moves your technical debt with the application. Worse, it converts capital expenditure into operating expenditure without any of the agility that's supposed to justify it: you're using the same processes, the same tools, and the same deployment cadence, just on someone else's hardware. If you rehost and stop, you have made your architecture worse and your bill more visible. The rehost is only defensible as step one of a two-step plan.
Repurchase Stop running it; buy it.
What it is: replace a custom or licensed on-prem application with a SaaS product. CRM, ERP, HR, ticketing, expense management — anything where your custom version is not a competitive advantage.
Cost: the migration engineering is small; the organisational cost is large. You are changing how people work, migrating data into someone else's schema, rebuilding integrations, and renegotiating a licence. Budget for change management, not for servers.
The trap: repurchase has the widest blast radius of any R on other applications. Every system that integrated with the old app now integrates with a different API, a different identity model, and a different data shape. In a dependency graph, repurchasing a node with five inbound edges means five more pieces of work you didn't plan. Check the in-degree of a node before you repurchase it.
Replatform Lift, tinker, and shift.
What it is: move the application and swap infrastructure components for managed equivalents — without changing application code or architecture. Self-managed PostgreSQL becomes a managed database service. A hand-rolled load balancer becomes the platform's. Files on an NFS mount become object storage. The app still thinks it's the same app.
Cost: moderate and, crucially, predictable. Each swap is bounded and independently testable. This is the best effort-to-benefit ratio on the whole curve, which is why it sits where it does in the Tab 0 diagram.
Replatform versus refactor — the line that people constantly blur: replatform changes what the application runs on; refactor changes what the application is. Containerising a monolith is a replatform: the code and architecture are untouched, the packaging changed. Splitting that monolith into services is a refactor. If you find yourself opening a source file to change business logic, you have crossed the line.
The trap: managed services have opinions. A managed database may not allow the extension, the superuser grant, the stored procedure, or the exact minor version your application assumed. Each of those is a small code change, and small code changes are how a replatform quietly becomes a refactor with no budget line.
Refactor Change what it is.
What it is: restructure the application's architecture to actually use the cloud — decompose the monolith, externalise state, make it replicable and stateless, adopt managed backing services. This is the only R that repays technical debt rather than relocating it.
Cost: the highest by a wide margin, and the least predictable, because you are discovering undocumented behaviour as you go. It also requires operational readiness you may not have: a delivery pipeline, independent deploys, distributed tracing, and people who have done it before.
When it's worth it: the application changes often, the change is painful, and the pain is costing the business measurably. If an application is stable and rarely touched, refactoring it is a hobby.
The trap: refactor shades into rebuild without anyone declaring it. The patterns catalog is blunt here — an application can be so encrusted that "refactoring" effectively becomes replacing it, and if replacement is genuinely necessary it is easier to build fresh than to keep pretending. Say which one you're doing, out loud, before you start. And when you do refactor, do it incrementally: that's Session 2.
Distribution. A common shape for an enterprise portfolio, reported by practitioners running AWS Migration Acceleration Program engagements, is roughly 40–50% rehost, 20–30% replatform, 10–15% refactor, and the remainder split across retire, retain, repurchase and relocate. If your plan is 80% refactor, you have written a wish; if it's 100% rehost, you have written a relocation invoice.
The R is not permanent. The most useful mental model is that R's compose over time: rehost now to hit the lease date, replatform in year two once the estate is stable, refactor the two applications that actually justify it in year three. AWS Prescriptive Guidance says this explicitly for large migrations — refactor is not recommended during a large migration precisely because it's hard to manage across many applications at once; rehost, relocate or replatform, then modernise afterwards.
Where it breaks. Two failure signatures worth recognising:
- The R was chosen by the wrong person. Architects pick refactor; finance picks rehost; the app owner picks retain. The choice needs all three inputs, and the tiebreaker should be the application's change frequency, because that's what determines whether architectural improvement pays back.
- The R was chosen once and never revisited. Discovery changes the picture. An app you scoped as rehost turns out to depend on a physical licence dongle; that's a retain, and it may drag three dependents with it.
The one honest three-cloud mapping in this session. There is no product for "choose an R," but all three vendors publish a structured adoption/migration framework, and they genuinely differ in emphasis:
| Vendor framework | AWS | Azure | Google Cloud |
|---|---|---|---|
| Named framework | AWS Cloud Adoption Framework (CAF) + Migration Acceleration Program (MAP) | Cloud Adoption Framework for Azure, with a distinct Migrate methodology | Google Cloud Adoption Framework, plus the "Migration to Google Cloud" guide series |
| Phase model | Assess → Mobilize → Migrate & Modernize (the AWS source teaches a six-phase variant adding Operate and Optimize) | Strategy → Plan → Ready → Adopt (Migrate / Innovate) → Govern → Manage | Assess → Plan → Deploy → Optimize |
| Strategy vocabulary | The 7 R's, formally documented in Prescriptive Guidance | Rehost / refactor / rearchitect / rebuild / replace (five, Gartner-derived) | Lift-and-shift / improve-and-move / rip-and-replace (three) |
| What it's actually good for | Portfolio mechanics and large-scale execution. The most detailed on wave planning. | Organisational readiness and governance. Strongest on landing-zone design. | Concise and architecture-led. Fewest ceremonies, least prescriptive on programme management. |
Sources: AWS Prescriptive Guidance — migration strategies Microsoft Cloud Adoption Framework Google Cloud — Migration to Google Cloud
▸ assessment
Assessment: you cannot sequence what you haven't mapped
Before movers arrive, somebody walks through the house and writes on each box which room it goes to, whether it's fragile, and what it's connected to. Skip that and the movers make the decisions for you — badly, at speed, at 6pm.
Assessment is that walk-through. Its output is not a spreadsheet of servers. Its output is a dependency graph with an R written on every node.
Assessment collects four things, in this order:
What exists
Servers, VMs, their OS versions, CPU/memory/disk, and — critically — utilisation over time, not a point-in-time snapshot. Right-sizing off peak-hour numbers is how you overspend by 40%.
What talks to what
Network flow data over weeks. Two boxes exchanging packets on port 1521 is a dependency even if no human knows about it. This is where the ghosts and the surprise couplings surface.
How heavy, how hot
Dataset sizes, change rates, and residency constraints. Size sets transfer time; change rate sets whether you can do a one-shot copy or need continuous replication.
What forbids what
Licences, compliance boundaries, change-freeze windows, and the maximum tolerable downtime per application. Downtime tolerance is the number that decides your cutover technique.
Three kinds of dependency edge behave very differently, and mixing them up is the single most common wave-planning error:
Agent versus agentless discovery. Agentless collectors read from the hypervisor or the network and give you inventory and coarse flows with no footprint on the guests. Agents give you process-level detail — which process opened that socket — which is what you actually need to attribute a flow to an application. Agents also require change approval on hundreds of production servers, which in a regulated environment is a months-long conversation. The usual answer is agentless across the estate, agents on the twenty machines you're most worried about.
How long to collect. Two weeks is the common default and it is too short. It catches daily and weekly patterns and misses everything monthly, quarterly, and annual. If your deadline permits, collect for a full quarter. If it doesn't, compensate by reading scheduler configuration directly — crontabs, scheduled tasks, job-scheduler definitions — because those declare the periodic dependencies that flow capture will miss.
The thing assessment can't tell you. Tooling reports what the estate does. It cannot tell you what the business needs, what the maximum tolerable downtime is, or whether an application is strategic. Those come from humans, and they are the inputs that decide the R.
▸ data gravity
Data gravity: the force that reorders your plan
Moving a person is easy. Moving a person's library of ten thousand sources is not. So when someone with a huge library relocates, the materials go first and everything else arranges itself around when they arrive — and if the materials genuinely can't move, the person ends up living near the materials.
That's data gravity, a term coined in 2010. Large datasets are slow and expensive to move, so applications and services accumulate around them rather than the reverse. Mass attracts.
Gravity has three separate components, and they don't always point the same way:
The arithmetic is worth having in your head, because it converts an argument into a number. Work in decimal units and remember that 1 TB = 8,000 gigabits:
Transfer time. days = TB ÷ (10.8 × Gbps × efficiency). The 10.8 is
just 86,400 seconds ÷ 8,000 gigabits-per-TB. Use efficiency ≈ 0.7 for a dedicated link you control
and ≈ 0.5 for anything shared.
Egress cost. USD ≈ TB × 1,000 × per-GB rate. At mid-2026 list
rates that's roughly $0.09/GB on AWS, $0.087/GB on Azure, $0.12/GB on GCP Premium Tier for the
first tier of internet egress — so about $900, $870 and $1,200 per 10 TB respectively. Ingress is
free on all three.
Rates as published mid-2026; tiers reduce the rate at volume and all three vendors now waive exit egress under specific conditions — see S3 for the fine print.
Gravity is now shaping architecture, not just schedules. The interesting shift over the last couple of years is that at AI scale the old assumption — move data to compute — has inverted for a growing class of workloads. Moving hundreds of terabytes across clouds can cost on the order of the compute itself, sovereignty rules increasingly forbid it outright, and open formats have made compute more portable than data ever was. The pattern that's emerging is the opposite of the one you were taught: put the workload where the data already lives.
Beware fake gravity. Not all bytes are equally heavy. A dataset is only gravitationally significant if it is irreplaceable at the destination. Ten terabytes you can re-derive, re-download from a public source, or regenerate has almost no gravity, however large it looks in the inventory. This distinction matters a great deal for your own workloads and it's the core of the apply-it section below.
The escape hatches. When gravity is genuinely too strong, you have four moves, in increasing order of commitment: (1) don't move the data — put compute next to it via a hybrid link; (2) move it physically on an appliance rather than over the wire; (3) move only the derived/aggregated form and leave the raw data behind; (4) accept a period of dual residency where both copies exist and you pay for both. Option 4 is the most common and the least planned-for.
▸ waves & cutover
Wave planning and cutover
You don't move a hospital in one night. You move a ward at a time, keeping every patient treated throughout, and you pick the first ward because it's the one where a mistake is survivable — not because it's the most important.
A wave is a batch of applications that move together in one window. A move group is the set of applications that must move together because splitting them would break something. Waves are a planning choice; move groups are a physical fact you discover during assessment.
Wave planning follows six rules, in priority order. When they conflict, the earlier rule wins.
Retire first, so you never plan work you don't need
Every retirement removes a node and its edges from the graph, which can dissolve a move group entirely. Retiring is also the only migration activity with no rollback risk, so it costs nothing to do early. Do it before you size waves, not after.
Wave 0 also contains the non-application work: the landing zone must exist, the hybrid link must be up and tested, identity federation must work, and the first target account or project must be governed. Migrating into an ungoverned destination is how you produce an estate nobody can audit.
Move groups are atomic
Applications sharing a database schema, a file share with tight write coupling, or a distributed transaction move in the same wave. There is no clever sequencing around a shared schema — you either move both, or you split the data first, which is a refactor and belongs in Session 2.
This is the rule people break. A shared-schema pair gets split across two waves because they have different owners, and then a fortnight of production runs with cross-WAN database traffic while everyone wonders why the batch window doubled.
Move dependencies before their dependents — usually
The default is leaf-first: move the things nothing else depends on, then their consumers. That way each application arrives to find its dependencies already in the target, and you're never reaching backwards across the gap.
The caveat: the leaf is often shared identity or a shared database — the thing with the highest blast radius. Moving it in wave 1 means your riskiest component moves before your team has learned anything. Two legitimate answers: move a low-risk leaf first purely for learning, or accept a temporary hybrid link so a dependent can move before its dependency. The private link you built in wave 0 is precisely what buys you the freedom to violate strict ordering.
The heaviest dataset in a group sets that group's date
Run the transfer-time formula for every dataset in the wave. Whichever is largest determines when replication must start, and therefore when the wave can cut over. A group with an 8 TB database on a 1 Gbps link cannot cut over sooner than the seeding takes, no matter what the project plan says.
Sequence heavy-data groups so their seeding overlaps other waves' execution. Replication is mostly wall-clock time, not people time — which means it should be running in the background while your team is doing something else.
Wave 1 exists to teach you, not to deliver value
Pick something with real users but survivable failure, few dependencies, and an owner who will co-operate. The point of wave 1 is to discover everything your runbook got wrong while the cost of being wrong is low: the change-approval process, the firewall rule nobody documented, the DNS TTL that's set to 24 hours, the monitoring that doesn't follow the app.
This is the patterns catalog's Start Small, and its companion Pave the Road: whatever wave 1 teaches you becomes the template, pipeline and runbook that waves 2 through 20 reuse. A migration where every wave reinvents the process is a migration that never gets faster.
Establish a rhythm, then measure velocity in applications per wave
Fixed-length waves — commonly two to four weeks — with a fixed sequence of activities: prepare, replicate, test, cut over, validate, hand to operations. The value of a fixed cadence is that velocity becomes measurable, and "how long will it take?" becomes arithmetic instead of a guess.
Expect the curve to be slow, then fast, then slow again: early waves are slow because you're learning, the middle is fast because the road is paved, and the last waves are slow again because what's left is what everybody avoided.
Cutover: the only irreversible hour
Everything up to cutover is reversible at near-zero cost, because the source system is still serving traffic. The technique you can use is decided by exactly one number: how much downtime the application can tolerate.
| Downtime tolerance | Technique | How it works | What it costs you |
|---|---|---|---|
| Hours–days | Cold copy | Stop the app, copy everything, start it in the target. | Nothing technically. The business pays in outage. |
| Minutes | Continuous replication + short freeze | Replicate continuously while the source runs; freeze writes, ship the final delta, flip. This is what every VM-replication service does. | Replication infrastructure and agents for the duration. Careful DNS TTL management. |
| Seconds | Change data capture with a read-only window | The database service streams changes into the target; you drain in-flight transactions and switch the connection string. | Schema constraints — CDC has opinions about primary keys and DDL changes during replication. |
| Zero | Dual-write / parallel run | Both systems accept writes; traffic shifts gradually; either can be authoritative. | By far the most expensive. You are building and operating a distributed system to perform a migration. Covered in Session 2 — and it is a modernization technique, not a migration one. |
DNS TTL. A record with a 24-hour TTL means some clients keep hitting the old endpoint for a day after you flip. Drop TTLs to 60 seconds days before the cutover, not during it. And remember that some clients — particularly older JVM configurations — cache DNS resolution for the life of the process regardless of TTL.
The source you left running. If the old system stays up "just in case" and still accepts writes, you now have a split brain and nobody has noticed. Firewall the old system's write path at cutover, keep it readable for comparison, and set a hard deletion date.
Licensing and hardcoded assumptions. Licences keyed to MAC addresses or hostnames. Certificates pinned to old names. IP addresses hardcoded in configuration files that no longer exist in source control. These are discovered at 03:00 on cutover night unless you find them during a test launch — which is precisely what test launches are for.
Wave fatigue. Fixed cadence means the same people run a cutover every fortnight for a year. Rotate the on-call role, keep a written runbook rather than tribal knowledge, and treat the third-to-last wave with the same discipline as the first — the failure rate goes up at the end, not down, because the remaining applications are the ones everybody deferred.
check
Worked on paper: sequencing a five-application portfolio
A mid-size financial services firm. Data centre lease ends in fourteen months. Five applications, one shared Oracle database, and a 40 TB tick-data archive sitting on an NFS filer. Here is the whole exercise: map it, choose an R per node, form move groups, then sequence.
Step 1 — the graph
Step 2 — an R per node, with the reasoning
| Application | Decision | Why this R and not the neighbouring one | Data weight |
|---|---|---|---|
| REPORT-PORTAL | Retire | Twelve users, no commits in three years, and every report it produces is reproducible from LEDGER. Dark-period it through one quarter-end first — a reporting tool is exactly the kind of thing someone uses once every three months. | — |
| AUTH-SSO | Rehost then repurchase, year 2 |
Repurchasing to a managed identity provider is clearly right long-term. But it has three inbound dependents, so repurchasing it now adds three integration projects to a fourteen-month deadline. Rehost to hit the date; repurchase once the estate is stable. This is the R composing over time. | Trivial |
| LEDGER-CORE | Replatform then refactor — Session 2 |
It changes weekly and generates most of the defects, so it is the one genuine refactor candidate in the portfolio. But refactoring during a deadline-driven migration is the classic mistake. Replatform now: containerise the app tier, move Oracle to a managed Oracle-compatible service. Refactor afterwards, incrementally. | 8 TB — sets the wave date |
| RECON-BATCH | Rehost | Nothing to gain from changing it, and it can't be separated from LEDGER's schema anyway. It moves when LEDGER moves. Splitting the schema so it could move separately is refactor work — legitimate, but not on this timeline. | Small — writes to a file share |
| PARTNER-API | Replatform | Stateless, talks only over REST, no local state. This is the easiest node in the graph: build a container image, point it at the new LEDGER endpoint, done. It could go in almost any wave. | None |
| TICK-ARCHIVE | Data move | Not an application — 40 TB of cold data. It has no cutover, no downtime constraint, and no dependents that are latency-sensitive. Which means it can start moving on day one and finish whenever. Never let cold data sit on the critical path. | 40 TB — heavy but not urgent |
Step 3 — the wave plan, with the arithmetic behind the dates
LEDGER's 8 TB over a 1 Gbps dedicated link at 70% efficiency: 8 ÷ (10.8 × 1 × 0.7) = 1.06 days for the initial seed. That's fine — so why does wave 2's bar show weeks of seeding? Because the seed is the easy part. The weeks are consumed by schema conversion validation, running change data capture long enough to prove the delta stays small, and rehearsing the cutover twice. Transfer time is rarely the binding constraint on a database move; confidence is.
The 40 TB archive over the same link: 40 ÷ 7.56 = 5.3 days of continuous transfer — but it would saturate the link that wave 2 needs. So it's throttled to run overnight only, stretching to several weeks of wall-clock. Which is completely acceptable, because nothing waits for it. Throttling a non-urgent transfer to protect an urgent one is a wave-planning decision, not a networking one.
If the link were 100 Mbps instead: the 40 TB becomes 40 ÷ (10.8 × 0.1 × 0.7) = 53 days at full utilisation. At that point the physical appliance stops being an exotic option and becomes the obvious one. That crossover is computed properly in S3.
Three cited examples worth knowing
- Capital One's portfolio approach. The patterns catalog cites Capital One's public
accounts of their cloud journey specifically as an example of rehosting being one of several
strategies applied across a portfolio — not the strategy. It's the cleanest available illustration
of the "R per app, not per portfolio" principle at genuine enterprise scale.
the cloud application patterns catalog, "Lift and Shift" examples. - Dress4Win — the case study your GCP source actually has. GCP guidance walks the
Google Professional Cloud Architect case studies, and Dress4Win is the migration one: a traditional
enterprise moving on-prem applications to GCP while maintaining security and minimising downtime. The
source's guidance for it is precisely this session's content — recommend phased migration, start with
non-critical workloads, use VPN/Interconnect for hybrid networking during the transition. Worth
reading as the exam-shaped version of the wave plan above.
a leading GCP architecture reference, "PCA Case Studies." - Wave planning became a product this year. The strongest evidence that this practice
is now standard: Azure Migrate shipped a public-preview capability literally called Wave Planning, which
breaks large migrations into manageable waves of workloads and applications. AWS Transform's VMware
agent likewise now performs dependency mapping and "intelligent wave planning" as an orchestrated step.
Five years ago this was consultancy IP in a spreadsheet; it's now a console feature on two clouds.
What's new in Azure Migrate AWS Migration & Modernization blog
your context
Apply it — your stack, and one lab
Run your own stack through the which-R tree and something interesting happens: it exits early and cheaply at almost every node. Your vLLM inference service is stateless, already containerised, and its state is externalised to object storage. That combination means Q6 — "can you get most of the benefit without opening a source file?" — answers yes immediately. It's a Replatform, and a shallow one: rebuild or re-tag the image, re-point the weights path, re-create the autoscaling policy. On the Tab 0 axis you are already sitting near the top-right without having done the expensive climb, because the application was born there.
That is not luck and it's worth naming precisely, because it's the whole argument for cloud-native architecture stated in migration terms: the properties that make an application easy to operate are the same properties that make it easy to move. Statelessness means no cutover data loss. Externalised state means the data migration and the app migration are separate, independently schedulable projects. Containerisation means the compute target is interchangeable. Your Modal deployment, your Vertex AI script, and a KServe deployment on GKE are three bindings of the same artefact.
Now find the real gravity. Run the four-item assessment checklist over your own setup and the answer is not what the inventory suggests. Gemma's weights are large but they have fake gravity — they are re-downloadable at the destination, so moving them is a bandwidth question, never a risk question. The genuinely heavy objects are the ones you cannot re-derive: a fine-tuning dataset you built, your eval suite and its golden outputs, and accumulated request/response telemetry. Those are usually far smaller in bytes and far heavier in gravity. If you ever plan a real cross-cloud move, inventory by irreplaceability, not by size.
Optional hands-on lab — a wave plan for something you actually run
This session is concept-only, so the lab is deliberately a paper exercise with a small cloud component. It costs nothing if you follow the guard rails, and it produces an artefact you'll reuse in S3.
Map your own stack as a graph
Take your Modal + Vertex + local vLLM setup and draw it as the dependency graph above. Nodes: the inference service, the weights bucket, the eval dataset, any gateway or proxy, your telemetry sink. Edges: label each one as API, shared-store, or batch.
Then write an R on every node and identify the move groups. You'll find at most one — and that is the point.
Measure a real transfer rate
Before you trust the formula, calibrate it. Push a single 1–5 GB file to a bucket, time it, and compute your actual achieved throughput. Compare it to your link's rated speed — the ratio is your real efficiency factor, and it is almost always worse than 0.7 on a shared connection.
Then use your number, not the reference text's, for the rest of this stage.
- Set a budget alert first. Before creating anything: a budget with an alert at a threshold low enough to be annoying — a few dollars. On every cloud this is free, takes two minutes, and is the only thing standing between you and a surprise.
- Smallest SKU, free tier only. This lab needs one storage bucket and one small file. No compute instance, no migration agent, no replication task.
- Ingress is free; egress is not. Uploading your test file costs nothing. Downloading it back is billed per GB. Upload, measure, and don't pull it down again.
- Teardown, in this order: delete the test object → delete the bucket → confirm the bucket is gone in the console, not just in the CLI response → leave the budget alert in place for the rest of the stage.
Part B uses object storage only. S3's lab involves migration-service agents and replication tasks, which bill continuously while they exist — the teardown discipline there is considerably more important, and it's spelled out in that tab.
You've now got LEDGER-CORE sitting in the cloud on managed infrastructure, still a monolith, still generating 60% of the defects. The migration is done and the problem is not solved — which is exactly the lift-and-shift-then-stop trap this stage opened with.
Session 2 is how you get out of it without a big-bang rewrite: the strangler fig, one slice at a time, with a working system and a cheap rollback at every step.
session exists
The system is bad, it makes money, and you cannot turn it off
Every serious modernization starts from the same three facts, all true at once. Ignore any one of them and you get the wrong plan.
LEDGER-CORE from Session 1 is now in the cloud. It is still a monolith. It still absorbs 60% of the defect budget, it still takes four hours to test, and a one-line change still requires deploying the whole thing. The migration succeeded and the actual problem is untouched. Somebody — usually somebody smart, usually in their first month — is now going to say: "Let's just rewrite it properly."
They are describing the single most reliable way to destroy a software organisation.
In 1998 Netscape decided that Navigator 4 was too messy to maintain and rewrote the browser from scratch. Version 5 never shipped. Three years of development produced a buggy, feature-incomplete version 6 while Internet Explorer went from afterthought to total market dominance. Lou Montulli, one of Navigator's original engineers, later confirmed to a classic engineering essay that the rewrite was one of the major reasons he resigned. The rewrite didn't just fail technically — the company did not recover.
The pattern repeats with depressing consistency: big-bang replacement, a feature freeze on the old system, a timeline that doubles and then doubles again, and competitors gaining ground while the team rebuilds what already existed. Analyses of enterprise modernization programmes published this year put the failure-or-underperformance rate for modernization projects somewhere in the region of two-thirds to three-quarters. The rewrite is not a bold choice. It's the default choice, and it's usually wrong.
Concept-only. There is no product called "strangler fig." What does exist is documentation: Microsoft publishes Strangler Fig and Anti-Corruption Layer as named patterns in the Azure Architecture Center, AWS covers the strangler fig in Prescriptive Guidance, and Google's migration guides describe the same shape under different wording. Those are references, not products, and forcing a three-cloud table onto them would be theatre. The one place tooling genuinely enters is the routing layer — an API gateway, service mesh, or reverse proxy — and you already built those in Stages 4 and 6.
▸ the pattern
The strangler fig
A strangler fig germinates in the canopy of a host tree and sends roots down its trunk. Over years the roots thicken, fuse into a lattice, and take over the job of holding the canopy up. Eventually the host dies and rots away, and what's left is a hollow, self-supporting fig in exactly the shape of the original tree. At no point was there a moment when the forest had no tree standing there.
the refactoring literature borrowed the metaphor for software. You grow a new system around the old one rather than beside it, take over one responsibility at a time, and delete the old code as each responsibility transfers. There is never a day when the system is down, and there is never a version that hasn't been in production.
The patterns catalog adds a useful distinction: strangling is really two activities running in parallel. Abandonment — all new work happens outside the monolith, so it stops growing. Migration — existing functionality is progressively reimplemented outside it, so it starts shrinking. In rare cases where usage patterns change fast enough, abandonment alone finishes the job and the monolith simply becomes irrelevant.
Mechanically the pattern has exactly one prerequisite and then four repeating phases. The prerequisite is a facade: a routing layer through which all client traffic passes before it reaches either system. Without it you cannot redirect a slice, and without redirection there is no strangler fig — just two systems.
Put a facade in front of everything and change nothing else
Insert a proxy between all clients and the monolith. Initially it passes 100% of traffic through unmodified. Nothing behaves differently, no feature moved, and the deploy is trivially reversible. The patterns catalog calls this Wrap the Monolith.
Two things this buys you immediately, before any migration work: protection for existing clients — including third-party ones you cannot change — and an observability baseline. Every request now flows through one point where you can measure it, which means you finally learn which endpoints are actually used and how often. That data is what tells you which slice to take first.
Ship this phase on its own and let it bake for a fortnight. If it's going to break something, you want that to happen while nothing else is in flight.
Build the new service, route a trickle, compare, then commit
Choose one cohesive slice of functionality. Implement it outside the monolith. Then move traffic onto it in graded steps rather than a switch: shadow first (send a copy of real requests to the new service, serve the old one's response, compare in the background), then a small canary percentage, then all of it.
Two supporting pieces attach here. An anti-corruption layer at the boundary, so the monolith's data shapes don't infect the new service's model. And a monolith-to-microservice proxy for the reverse direction, so code still inside the monolith that used to call this functionality in-process now calls out to the new service instead. Both are covered below.
Repeat, and defend the directive that stops the monolith growing
Slice by slice, the facade's routing table shifts from mostly-legacy to mostly-new. This phase lasts months to years and its main enemy is not technical.
The technical work is a repeating loop you get faster at. The organisational work is holding the line on new features go outside the monolith. Adding to the monolith is always faster this sprint, which means without an explicit directive the monolith grows faster than you shrink it and the programme never converges. The patterns catalog documents both the carrot (templates, examples, paved pipelines that make the new way easier) and the stick (a governance review that must approve any change made inside the monolith). Real organisations end up needing both.
Delete the old code — and know when to stop
Once a slice has served 100% of traffic for long enough to trust, remove the legacy implementation. Not disable. Not comment out. Delete, and delete its tables too once nothing reads them. Code left behind gets accidentally re-enabled, gets patched during incidents, and keeps appearing in dependency scans for years.
Stopping deliberately is legitimate. Some parts of a monolith are stable, rarely changed, and already wrapped behind a clean facade — the benefit of extracting them is close to zero. The patterns catalog is explicit that complete elimination is the ideal, not the requirement. What is not legitimate is stopping by accident: doing the three interesting slices, losing executive attention, and running two systems indefinitely at double the maintenance cost. That is the single most-cited failure mode of this pattern.
The mechanism itself, in eight lines
The facade is not conceptual — it's a routing rule. Here is what shifting one slice actually looks like, expressed as a weighted split at the proxy. This is the artefact the entire pattern rests on:
# facade: move 5% of one slice to the new service, everything else untouched
location /api/statements/ {
split_clients "${remote_addr}${request_id}" $slice {
5% statement_svc; # canary share — raise 5 → 25 → 100 over days
* monolith; # default stays legacy until you say otherwise
}
proxy_pass http://$slice;
}
# every other path falls through to the monolith, unchanged
Three properties make this the right primitive. It is reversible in one deploy — change 5% to 0% and you are back. It is sticky per client, because the split key includes the client address, so a given user doesn't flip between implementations mid-session. And it is observable: the proxy emits which branch served each request, so your comparison data comes for free. A feature flag inside the monolith gives you none of the first and only some of the third.
You run two systems for a long time. This is the real price, and it is not small: two deployment pipelines, two monitoring stacks, two on-call rotations, two sets of skills, and a technology diversity that raises total cost of ownership for the duration. Teams reject the pattern on these grounds and they are not being stupid — they are just comparing it to a rewrite that finishes on schedule, which is a fictional comparison. Against a rewrite that actually finishes when rewrites actually finish, strangling is cheaper as well as safer.
The benefits arrive late and the costs arrive early. Because it's gradual, you carry the overhead of two systems from month one and only reach a materially better architecture in year two. This is a funding and attention problem more than an engineering one, and it's why the pattern needs an executive sponsor who understands what they've signed up for.
Data is where it genuinely gets hard. A monolith uses a centralised database; services want to own their own stores. Every slice you extract raises the same questions: consistency across two stores, transactions that used to be one commit and now aren't, queries that used to be a join and now need two calls, and reporting that assumed everything lived in one schema. The cross-service transaction that used to be a database transaction typically becomes a saga. Budget for this explicitly — the data work is usually larger than the code work.
When it doesn't apply. The pattern needs a chokepoint. If clients call the system as an in-process library, or link against it, or hit hardcoded addresses you cannot change, there is nowhere to put a facade. Your first job then is not strangling — it's refactoring internal boundaries until a facade becomes possible, which is the patterns catalog's Refactor the Monolith as a precondition rather than a goal.
What the numbers say. Published research on incremental modernization is encouraging but should be read with the usual caution about vendor-adjacent studies: Kyndryl's 2025 research reported an average ROI around 288% for organisations modernizing mainframe applications using incremental approaches, and McKinsey's analysis of programmes completed in 2024–25 reports infrastructure cost reductions in the 30–50% range with 20–30% improvement in development cycle times. Treat those as evidence that the approach works, not as a forecast for your programme.
▸ the boundary
Keeping old and new decoupled: facade, ACL, and the reverse proxy
Three distinct pieces sit on the boundary between the monolith and the new services. They are routinely confused with each other, and the confusion produces real architectural damage, so it's worth separating them precisely.
Facade
Sits in front of everything. Routes each incoming request to the monolith or a new service. Translates protocols where old clients speak an old dialect.
Protects: your clients, from knowing a migration is happening at all.
Anti-corruption layer
Sits inside the new service at its edge. Translates the monolith's data shapes, naming and semantics into the new service's own domain model.
Protects: the new service's design, from being colonised by legacy concepts.
Monolith-to-microservice proxy
The old component keeps its exact signature inside the monolith, but its body now just forwards the call to the new service.
Protects: the hundred call sites inside the monolith you don't want to edit.
You've hired a brilliant translator to work with a foreign partner whose contracts use archaic terminology, inconsistent units, and one field that means three different things depending on a flag elsewhere in the document. The translator's job is not to pass the archaic terms through with a footnote. It is to produce a clean document in your language, so that everyone downstream can reason normally without learning the partner's dialect.
That is the anti-corruption layer. Note the name: the thing being prevented is corruption of
your model, not corruption of data. Without it, the monolith's ST_FLG column with its
values 'A', 'C' and 'X' propagates into your new service, then into its API, then into the next three
services that consume it, and five years later somebody is still asking what 'X' means.
Concretely an ACL is a translation function at the service's edge, and the whole discipline is that the legacy vocabulary must not survive the crossing. Nothing past this function knows the monolith exists:
# anti-corruption layer: legacy shapes stop here and go no further
def to_statement(row): # row = raw LEDGER_STMT record
return Statement(
id = StatementId(row["STMT_ID"].strip), # padded char → typed id
period = Period.from_yyyymm(row["PRD"]), # "202607" → Period
balance = Money(row["BAL_CENTS"], row["CUR_CD"]), # int + code → Money
status = STATUS_MAP[row["ST_FLG"]], # 'A'/'C'/'X' → enum
) # unknown flag ⇒ raise, don't pass through
Four things that small function is doing, each of which is a boundary you'd otherwise leak: normalising a padded fixed-width identifier into a typed one; converting an encoded date into a real domain concept; combining two columns that only mean something together into a single value object; and turning an opaque flag into a named enumeration. And the last line matters most — an unmapped flag raises rather than falling through, so unknown legacy states surface as errors during the shadow phase instead of as silent nulls in production.
Facade versus monolith-to-microservice proxy — when to use which
The patterns catalog separates these carefully and the distinction is practical. A facade intercepts traffic arriving from outside. It cannot intercept calls made inside the monolith, because those are in-process method calls that never touch the network. So when you extract a component that other monolith code calls, you have two options:
| Approach | What you change | Choose it when | Cost |
|---|---|---|---|
| Rewrite the callers | Every call site inside the monolith is edited to call the new service directly. | There are few call sites, and you own them all. | Cleanest end state. Linear in the number of call sites, and each edit is a chance to break something. |
| Monolith-to-microservice proxy | Nothing at the call sites. The extracted component keeps its signature; its body becomes a forwarding call. | There are many call sites, or the calling code is fragile, or you want the change reviewable in one file. | One extra network hop, plus the monolith must be rebuilt and redeployed each time you extract. Distributed-system failure modes now exist inside code that never had them. |
In practice most teams use both: the proxy first, to get the extraction shipped, then rewrite call sites opportunistically whenever they're touching that code anyway. And note the proxy can be asynchronous — the old synchronous method can publish to a queue that the new service consumes, which is exactly how the retail example in the patterns catalog fixed a Black Friday thread-pool collapse by decoupling order capture from order writing.
The ACL has an expiry date, and you should write it down. While the monolith exists, the ACL earns its keep. Once the legacy source is gone, an ACL that survives is just an extra mapping layer nobody remembers the reason for. Tag these classes so that "delete the ACL" is a visible item on the decommissioning checklist, not an archaeological discovery in 2031.
Facades accumulate responsibilities. A facade is an attractive place to put things: routing, protocol translation, authentication, rate limiting, caching, circuit breaking. Each is defensible; collectively they turn your routing layer into a second monolith that every team must change and nobody owns. Keep policy in the facade and keep business logic out of it. The test: if changing a business rule requires a facade deploy, the rule is in the wrong place.
Every hop is a new failure mode. An in-process call that could not fail is now a network call that can time out, retry, and partially succeed. Adding a proxy hop to a call path that was previously a method invocation changes its reliability characteristics, and the code around it was written assuming it couldn't fail. Timeouts and circuit breakers at these boundaries are not optional polish.
▸ finding slices
Where do you cut? Hairline cracks and the three extraction moves
A stonemason splitting a block doesn't hit it in the middle. They look for the fracture plane — the line along which the material already wants to separate — and apply force there. Hit the wrong place and you get rubble.
Monoliths have fracture planes too. The patterns catalog calls them hairline cracks: places where functionality is already more cohesive internally than it is coupled externally. Finding them is most of the skill; the extraction itself is comparatively mechanical.
Three categories of crack are easy to spot, in decreasing order of luck:
Once you've found a crack, exactly three moves are available. Which one you use is determined by how coupled the target is, not by preference:
| Move | Precondition | What you do | Risk profile |
|---|---|---|---|
| Extract Component | The functionality is already loosely coupled with a defined interface. | Copy the code out, run it as a service, route callers through a facade or proxy. Often you extract a large macro service first — deliberately bigger than a microservice — and split it further once it's stable outside. | Lowest. Behaviour is preserved because the code is preserved. |
| Refactor then Extract | Partially coupled — a boundary is visible but not clean. | First reorganise inside the monolith into a module with a real interface (a facade internally, then a proper service API), update internal callers to use it, and only then pull it out. | Medium. Every refactoring step must be behaviour-preserving, which requires test coverage you may not have. |
| Replace as Microservice | Tightly coupled, spread across the monolith, impossible to extract. | Freeze the functionality in the monolith, reimplement it from scratch outside, validate against the original, canary-release, then redirect and eventually delete. Branch-by-abstraction is a specific variant of this. | Highest. You are rewriting, and the original is your only specification — which is exactly why playback testing exists. |
A useful diagnostic for locating candidates that the material recommends and that costs almost nothing: look at your commit history and your issue tracker. The parts of the monolith that change most often and break most often are, almost by definition, the parts where extraction pays back fastest. Static analysis tools that surface coupling and code smells help, but change frequency is the better signal because it's about cost, not aesthetics.
Macro services are a feature, not a compromise. There is real pressure to extract "proper" microservices immediately. Resist it. Pulling out one large, internally-tangled component is a genuine win — it is now independently deployable and independently scalable — and you can split it later with far better information, because you'll have watched it run. The patterns catalog's airline example did exactly this: the ticket purchase process came out as one large service with messy internal switches, and was refined over several later releases.
Two orderings, both valid. Bottom-up means starting with the obvious low-hanging fruit — fast wins, good for morale and learning, but the resulting service boundaries may not follow the domain. Top-down means starting from business capabilities and carving downward — better boundaries, much slower start. Most successful programmes are bottom-up for the first few slices to build capability and credibility, then switch to top-down once the team understands the domain well enough to model it.
Right-size against the domain, not against a line count. Whichever move you use, the boundary should follow a bounded context from C6. A service split along a technical seam that cuts through a domain concept will need chatty coordination with its neighbour forever, and that chattiness is permanent.
▸ the data
Dual-write, backfill, reconcile: moving state without losing any
Changing banks. For a while you keep both accounts open. New income goes into both — well, you can't actually do that with money, which is precisely why this is hard in software too. What you can do is: run both accounts, move the historical statements across, check the balances match for a couple of months, and only then close the old one. Nobody sensible closes the old account the day the new one opens.
Three separate activities hide in that story. Dual-write — new events land in both places. Backfill — the history the new place never saw gets copied across. Reconciliation — you prove the two agree before you commit to one.
The full sequence for moving one slice's data, with the source of truth marked at every stage. The whole design principle is that exactly one store is authoritative at any moment, and the switch of authority is a single explicit step — never an emergent property of routing weights.
What each stage is actually for
Reads first, because reads are safe
Reads can be wrong without destroying anything. Serve them from the new service — initially against a read replica of the monolith's own database, so there is no new store yet and no consistency question at all. Shadow the responses and diff them.
This is where you discover that the legacy system rounds differently, or returns records in an order somebody depends on, or has a bug that has become a documented feature.
Writes, and the history problem
Dual-write means the application writes both places. It is not a distributed transaction — the monolith commits first and remains authoritative, and a failed write to the new store is logged and repaired, never rolled back. Accepting that asymmetry is what keeps this simple.
Backfill then copies everything that predates the dual-write. Chunk it, make it idempotent, run it during quiet hours, and track a high-water mark so it can resume.
Reconciliation is the evidence
A scheduled job compares both stores and reports differences: row counts, checksums per period, and full field-level diffs on a sample. The output is a number that goes to zero and stays there.
This number, not a project milestone, is what authorises C6. It is also the artefact that convinces a risk committee, which matters more than engineers expect.
Flip, then actually finish
Switch authority to the new store. Keep dual-write running into the old store for a defined period so the monolith's reports keep working and a rollback is still theoretically possible.
Then finish properly: stop the dual-write, delete the monolith's tables for this slice, and delete the code. A slice is not migrated until the old tables are gone.
Playback testing: how you validate a rewrite with no specification
When you Replace as Microservice you are reimplementing behaviour that nobody fully understands, in a system with inadequate tests, where some of the bugs are load-bearing. The patterns source's answer is playback testing — also called a parallel run. Capture a set of real inputs and actions against the original system, replay the identical sequence against the new implementation, and compare the results.
Two practical forms. The simplest: run both against equivalent databases and diff the resulting state — feasible when the new schema is similar, harder when it isn't, in which case you write extraction scripts that normalise both sides before comparing. The richer form: capture live transactions as events over a period, transform them to the new service's API, and replay. If you're using CQRS, capturing calls to the write model gives you this almost for free.
The decision that catches people out is how often you compare. Comparing only final state at the end of a long replay is cheap and tells you little about where a divergence started. Step-by-step comparison after each transaction localises failures immediately and costs far more to build. Start with end-state comparison plus periodic summaries; add step-by-step only for the slices where you're finding divergences.
Dual-write is not atomic, and pretending otherwise is the classic bug. The monolith commits, then the write to the new store fails. If your code treats that as a transaction and rolls back, you've made a working system fail because of a system that isn't live yet. The correct behaviour is: commit to the materialitative store, attempt the secondary write, log failures to a repair queue, and let reconciliation catch what the queue misses. Your divergence count will be non-zero for a while. That's the design working, not a defect.
Backfill races live writes. While you're copying 2019's rows, 2026's rows are arriving. Chunk by a stable key, make every write an upsert keyed on identity, and never let backfill overwrite a newer value with an older one. Getting this wrong produces the worst class of bug in the whole pattern: silent, historical, and discovered months later during an audit.
Reporting is the slice everyone forgets. Reports join across everything. The moment one slice's data lives elsewhere, every report touching it breaks or silently under-reports. Plan for reporting explicitly — usually by keeping a consolidated read model fed from both sides for the duration, which is more work than it sounds and is never in the original estimate.
Deciding when to stop reconciling. There is no formula. What works is a written criterion agreed in advance — for example, zero unexplained differences across two consecutive month-end cycles — recorded before you start, so the decision isn't made under deadline pressure by whoever is most tired.
check
Worked on paper: strangling one slice of LEDGER-CORE
Same system as Session 1, now sitting in the cloud, still a monolith. We take one slice — statements — from first facade deploy to deleted legacy tables, and cost every step. The point of the exercise is to see how much of the calendar goes to confidence rather than to code.
| Week | Step | Reversibility | What could go wrong here |
|---|---|---|---|
| 1–2 | Facade in. Reverse proxy in front of LEDGER. 100% pass-through. No functional change. Ship it and leave it alone. | Trivial — remove the proxy. | A client somewhere pins the origin's TLS certificate, or an internal caller bypasses the proxy by IP. You find both in week 1, which is the point. |
| 3 | Read the traffic. Two weeks of proxy logs answer which endpoints are used, how often, and by whom. Statements turn out to be 31% of requests and almost entirely reads. | N/A — observation only. | You discover an endpoint nobody knew was live, called by a partner you thought had migrated in 2022. |
| 4–7 | Build statement-service. Reads only, against a read replica of LEDGER's database. ACL at the edge translating LEDGER_STMT into a real domain model. |
Nothing is live. | The ACL raises on three legacy status flags nobody can explain. Good — better now than in production. |
| 8–9 | Shadow. Mirror 100% of statement reads to the new service, serve the monolith's answer, diff asynchronously. | Free — stop mirroring. | 0.4% of responses differ. Two thirds are rounding on a currency conversion; one third is the monolith returning a field in a legacy order that a client's parser depends on. |
| 10 | Canary reads. 5% → 25% → 100% over the week, using the split rule from Pass 2. | One routing weight. | p99 latency doubles at 25%. The replica is under-provisioned. Caught at 25%, not at 100%. |
| 11–13 | Own the store. Stand up the service's own database. Dual-write from the application: monolith commits first and stays authoritative, secondary write logged on failure. | Stop dual-write; new store is disposable. | The secondary write occasionally fails under load. The repair queue handles it — as designed. Resist the urge to make it transactional. |
| 13–17 | Backfill. Seven years of statements, chunked by period, idempotent upserts, overnight only, resumable via high-water mark. | Truncate and restart. | Two periods in 2020 have duplicate keys the old schema tolerated and the new one doesn't. This is a data-quality discovery, and it is normal. |
| 18–24 | Reconcile. Nightly diff: row counts, per-period checksums, field-level on a sample. Target: zero unexplained differences across two consecutive month-ends. | Still free. Monolith is still the truth. | Month-end reveals an adjustment posting path that writes statements via a stored procedure, bypassing the application layer entirely — so it never dual-wrote. This is the finding that justifies the whole reconciliation stage. |
| 25 | Flip the source of truth. New store is authoritative. Dual-write continues into the old tables so legacy reports keep working. | One-way door. | Nothing, if weeks 18–24 were done honestly. Everything, if they weren't. |
| 26–30 | Finish. Migrate the two reports that read statement tables. Stop the reverse dual-write. Drop the legacy tables. Delete the monolith code and its ACL. Update the routing table. | Historical. | The step that gets deferred forever. Put it in the same ticket as the flip, or it will not happen. |
Thirty weeks for one slice. Roughly seven weeks of building (4–7 and part of 11–13) and twenty-three weeks of proving, waiting, and cleaning up. That ratio is not a sign of a badly run project — it is what the pattern is. You are buying reversibility, and reversibility is bought with calendar time.
Two things get much faster after the first slice: the facade already exists, and the shadow/canary/ dual-write/reconcile machinery is now reusable infrastructure. This is Pave the Road. A realistic expectation is that slice two takes half as long as slice one, and slices three onward reach a steady state — which is precisely why estimating a strangler programme from the first slice overstates it badly, and why the first slice should be chosen for its teaching value rather than its business value.
Three cited examples worth knowing
- Netscape, 1998 — the instructive failure. The rewrite of Navigator that produced
no version 5, a broken version 6 three years later, and a lost market. a classic engineering essay's
Things You Should Never Do, Part I is the canonical write-up, and Lou Montulli — one of
Navigator's original engineers — corroborated it directly. It's worth reading not for the history but
for the mechanism: the rewrite discarded years of accumulated bug fixes, each of which encoded a real
edge case that nobody had documented anywhere else. Legacy code is ugly because it is correct.
joelonsoftware.com — Things You Should Never Do, Part I - Netflix, Amazon, eBay, Uber — the successes, and what they share. The patterns
source collects these in §10 and the common thread is more interesting than any individual case.
Netflix rebuilt its video processing pipeline from a single monolithic media system into services
partitioned by business capability, and explicitly reports that monitoring and logging were what made
the transition survivable. Uber broke a monolith into hundreds of services during hypergrowth while
continuing to run. eBay migrated incrementally while shipping features on a normal cadence. In every
case the pattern was chosen because the business could not pause — not because it was
architecturally elegant.
the cloud application patterns catalog, "Strangle the Monolith" examples. - The 2026 twist: AI is changing the economics of the slow parts. The historically
expensive phases of this pattern are discovery, dependency mapping, and generating tests for code with
none — exactly the work now being automated. Airbnb migrated roughly 3,500 test files in six weeks
against an original estimate of eighteen months, using an AI pipeline with validation gates rather
than a blind find-and-replace. AWS reports Thomson Reuters modernizing 1.5 million lines of .NET
monthly. What is striking is that these tools have reinforced incremental migration rather
than enabling big-bang rewrites: the successful pipelines are file-by-file with test gates and human
review, which is the strangler fig with a faster inner loop.
Why big rewrites fail — case collection CIO Dive on AWS Transform, Oct 2025
your context
Apply it — your stack, and one lab
Your inference service is not a strangler-fig candidate, and understanding why is the lesson. A stateless container with externalised state has no monolith properties to decompose — nothing is entangled, so there is nothing to strangle. If you needed to replace it you would deploy the new version alongside, shift traffic, and delete the old one. That's a blue-green deploy, and the fact that it's trivially available to you is a direct consequence of the architecture, not of luck.
The fintech pipeline, if it were a legacy monolith, is the real target. And the slices would follow the same shape as the LEDGER walkthrough: find the reads, shadow them, own the store, dual-write, backfill, reconcile against a month-end. The finance domain has a specific hazard worth naming — every number is auditable, so a 0.4% response divergence that would be a shrug in a recommendation system is a blocking defect here. That's why the reconciliation gate in a financial strangler is two month-end cycles rather than two weeks.
The pattern you will actually use, though, is the facade. Model serving is heading the same way applications did: multiple model versions, multiple providers, and clients that must not know which is serving them. A routing layer in front of your endpoints — call it a gateway — that can shadow a new model against the current one, canary 5% of traffic, compare outputs offline, and roll back with one weight change is exactly the strangler facade with a different payload. The shadow-then-canary-then-commit sequence in the LEDGER table transfers to a model swap essentially unchanged. The one adaptation: your comparison step can't be an equality diff, because two model versions won't produce identical text, so the "reconciliation" becomes an eval-suite comparison with a quality threshold instead of a zero-difference gate.
Optional hands-on lab — build a working facade and strangle something
This runs entirely on your laptop with Docker. No cloud account, no cost, nothing to tear down — which is deliberate, because the concept is worth more than the console practice here.
Two backends, one proxy, a moving weight
Stand up two trivial HTTP services — call them legacy and new — each
returning its own name and a version. Put nginx in front with the split_clients rule
from Pass 2, starting at 0%.
Then drive traffic in a loop and walk the weight: 0 → 5 → 25 → 100, reloading the proxy between each. Watch the ratio shift in your request log. Then set it back to 0 and confirm you're fully rolled back in one reload.
The thing to feel: how cheap the rollback is. That cheapness is the entire argument of this session.
Add shadowing and find a divergence
Extend the proxy to mirror requests to new while still serving legacy's
response (nginx's mirror directive, or a few lines in front of both). Log both responses
and diff them.
Now introduce a deliberate subtle bug in new — round a number differently, or change
a field's order. Confirm your diff catches it without any user having seen it.
Then adapt it: swap the two backends for two model endpoints — a local vLLM server and a smaller model, say — and replace the equality diff with an eval comparison. You've just built the model-rollout facade described above.
- Keep it local. There is no reason to put this in a cloud account. If you do choose to run Part B against a GPU instance for the model variant, use the smallest GPU SKU available, use spot/preemptible, and set a budget alert first.
- Teardown for the GPU variant, in order: stop the inference containers → delete the instance or scale the node pool to zero → verify in the console that no GPU node is still running → delete any load balancer you created, since those bill hourly on their own and survive the instance that prompted them.
- The trap specific to this lab: a proxy or load balancer left running is the cheapest thing to forget and one of the more annoying line items to find later. If you created one, it goes on the teardown list before you close the terminal.
You can now choose an R per application, sequence a portfolio into waves, and decompose a monolith incrementally with a working system at every step. All of it vendor-neutral, and all of it decided before anyone opened a console.
Session 3 is the machinery: what AWS, Azure and GCP actually give you to move VMs, databases and bulk data, which hybrid platform fits which shape of problem, and which product names in the materials died while you weren't looking.
session exists
You've decided the R. Now something has to actually move the bits.
This is the session where products appear — deliberately last, because a tool cannot rescue a bad sequencing decision, and every one of these tools assumes you already know which R you chose.
The tooling divides into five layers, and they map almost one-to-one onto the decisions from Session 1. Discovery and assessment feed the R decision. VM replication executes a rehost. Containerisation tools execute a replatform. Database migration services execute the data half of everything. Bulk transfer handles data gravity. And hybrid platforms handle the applications you decided to retain — or the period where some things have moved and some haven't.
None of your four sources has current migration tooling, and the AWS one is actively misleading: it teaches AWS Server Migration Service and CloudEndure as the primary rehost tools across §26, including in its practice questions. Both are retired. It never mentions MGN at all — the service that replaced them in 2021, and which has itself been renamed since. Every product name, capability and price below is grounded in vendor documentation and news from the last six months, with links. Two of the changes landed within the last two weeks and one within the last day.
▸ the five layers
The five layers of migration tooling
A removals company doesn't sell you one product. There's the surveyor who walks the house and quotes (discovery and assessment). There's the van for furniture (VM migration). There's the specialist who dismantles and rebuilds the piano (containerisation). There's the separate service for the wine cellar, because bottles need temperature control and can't just go in a box (database migration). And there's the shipping container for the twenty tonnes of stuff going by sea because it won't fit in the van (bulk data transfer).
Then there's the storage unit you keep renting because some things aren't moving yet (hybrid). Every cloud sells all six. They differ in emphasis, not in the shape of the problem.
Layer 1 — Discovery and assessment
| Capability | AWS | Azure | Google Cloud |
|---|---|---|---|
| Central hub | AWS Migration Hub — tracks progress across AWS and partner tools in one dashboard | Azure Migrate — discovery, assessment and server migration in one service | Migration Center — the unified hub; replaced the older standalone assessment tooling |
| Inventory collection | AWS Application Discovery Service — agentless connector for VMware, or agents for process-level detail | Azure Migrate appliance (agentless for VMware/Hyper-V); plus Arc-based discovery preview for servers already Arc-enabled | Migration Center discovery client, agentless vSphere collector, or import from a CSV/RVTools export |
| Dependency mapping | Agentless network-level, or agent-based with process attribution | Agentless and agent-based dependency analysis | Dependency insight via the discovery client |
| Business case / TCO | Migration Evaluator; assessment agent within AWS Transform | Business case built into Azure Migrate, including an Arc business case preview | Migration Center produces cost estimates and a business case; Google positions these as AI-assisted |
| Wave planning | Documented in Prescriptive Guidance; AWS Transform's VMware agent performs dependency mapping and automated wave planning | Wave Planning preview — an explicit console feature that breaks a migration into waves of workloads | Grouping in Migration Center; no dedicated wave-planning product |
| Notable 2026 shift | All three are pushing agentic AI into this layer. AWS Transform reached GA in May 2025 with agents for VMware, mainframe, Windows/.NET and assessment; at the New York Summit in 2026 AWS announced a continuous modernization capability in preview. Azure has Copilot guidance at points in the migration journey. Google markets Migration Center's toolsets as AI-powered. Treat the marketing sceptically and the direction seriously: discovery and planning is genuinely the part of migration best suited to automation. | ||
Sources: What's new in Azure Migrate GCP Migration Center — migration tools AWS Transform
Layer 2 — VM and server replication (the rehost engine)
All three work the same way, and knowing the mechanism means you can reason about any of them: install a lightweight agent on the source machine; it replicates disk blocks continuously over an encrypted channel into a staging area in the target account; you launch test instances into an isolated network as often as you like without touching the source; and when you're satisfied you cut over, which stops the source and launches the real target from the latest replicated state. Because replication is continuous, the downtime at cutover is only the final delta — typically well under an hour.
| AWS | Azure | Google Cloud | |
|---|---|---|---|
| Current name | AWS Transform MGN renamed Jul 2026 formerly AWS Application Migration Service |
Azure Migrate: Server Migration current | Migrate to Virtual Machines current formerly Migrate for Compute Engine, originally Velostrata |
| What it replaced | AWS SMS retired and CloudEndure Migration retired — MGN is CloudEndure's technology, reengineered after AWS acquired the company in 2019 | The older Azure Site Recovery–based migration path | The Velostrata branding; the standalone Migrate for Compute Engine name |
| Sources supported | Physical, VMware vSphere, Hyper-V, Nutanix, KVM, bare metal, EC2 across Regions/accounts, and other public clouds | VMware, Hyper-V, physical, AWS and GCP instances | VMware vSphere, Hyper-V, physical, AWS and Azure instances |
| Mechanism | Continuous block-level replication via agent (agentless option available) into a staging subnet; automated machine conversion; test launch; orchestrated cutover | Agentless replication for VMware, agent-based otherwise; test failover; cutover | Replication of VM disks with a test-clone capability before cutover; CMEK supported |
| Typical downtime | Most applications under one hour at cutover | Minutes to an hour, depending on final delta | Minutes to an hour |
| Cost of the tool | Free for the first 90 days per source server; then replication charges. You always pay for target compute, storage and transfer. | Replication free for 180 days per machine; you pay for target resources and storage | No charge for the tool; you pay for target resources |
| 2026 addition | Two rehost paths. Self-directed through the MGN console, or agentic rehost via AWS Transform, where AI agents handle initialisation, IAM setup, launch templates, agent installation, inventory and network mapping per wave. Both use the same replication engine and you can switch between them mid-migration. | Enterprise Linux (RHEL, SLES) migration supported with Azure Hybrid Benefit; Azure VMware Solution assessments extended with new host types | Steady — no major 2026 repositioning |
Sources: AWS Transform MGN product page (page updated 15 Jul 2026) AWS — sunsetting CloudEndure and SMS Azure Migrate release notes GCP migration tools
Sections 26.4 and 26.8 present AWS Server Migration Service and CloudEndure as the two rehost options, with instructions for wiring CloudEndure into Migration Hub, and both appear as answer options in the section's practice questions. Neither is orderable. AWS announced the sunsetting of both and named MGN the primary recommended service for lift-and-shift migrations; CloudEndure was withdrawn from commercial Regions at the end of 2022. If a practice question offers SMS or CloudEndure as the "correct" rehost tool, the current answer is AWS Transform MGN. The material's underlying concepts — Migration Hub as central tracking, Application Discovery Service for inventory, DMS for databases, the six-phase process — remain accurate.
Layer 3 — Containerisation (the replatform engine)
| AWS | Azure | Google Cloud | |
|---|---|---|---|
| Tool | AWS App2Container for Java/.NET; AWS Transform agents for full-stack Windows/.NET modernization | Azure Migrate: App Containerization for ASP.NET and Java web apps | Migrate to Containers — CLI-based |
| How it works | Inspects a running application, generates container image and deployment artefacts, targets ECS, EKS or App Runner | Discovers the app on a running server, builds an image, targets AKS or App Service | Extracts the workload from a VM and generates artefacts you deploy to GKE or Cloud Run |
| Watch out | Refactor Spaces is closed to new customers; AWS Transform is now the path for anything beyond a straight lift-and-shift | Narrow language support — it is a web-application tool, not a general one | The console UI, migctl and the processing-cluster CRDs were removed in May 2024. The CLI on your local machine is the only supported path now. Older tutorials will send you to a UI that no longer exists. |
| Cost | No charge for the tool | No charge for the tool | No charge for the tool |
Source: Migrate to Containers and its release notes; AWS migration tool landscape 2026
Layer 4 — Database migration
Two distinct jobs hide under one heading. Homogeneous migration — Oracle to Oracle, PostgreSQL to PostgreSQL — is mostly a data-movement problem. Heterogeneous — Oracle to PostgreSQL, SQL Server to Aurora — additionally requires schema and code conversion, and that second part is where the effort actually is. A heterogeneous database migration is a refactor wearing a replatform's clothes: stored procedures, triggers, proprietary SQL dialects and application data-access code all need changing.
| AWS | Azure | Google Cloud | |
|---|---|---|---|
| Service | AWS Database Migration Service (DMS) | Azure Database Migration Service | Database Migration Service |
| Schema conversion | DMS Schema Conversion, and the downloadable Schema Conversion Tool | Data Migration Assistant for assessment and schema work | Conversion workspace within DMS, with AI-assisted conversion for Oracle sources |
| Near-zero-downtime mechanism | Full load then change data capture; source stays live throughout | Online migrations; for SQL Server, MI link distributed availability groups for real-time replication, or Log Replay Service via backup and restore | Continuous replication for MySQL, PostgreSQL and Oracle sources into Cloud SQL and AlloyDB |
| Notable 2026 development | Steady; DMS remains the workhorse and is correctly described in your AWS source | The Arc-based SQL migration experience. Continuous migration assessment went GA in July 2025; the integrated workflow to Azure SQL Managed Instance followed; SQL Server on Azure VMs was added as a target in March 2026 and reached GA in July 2026. Assessment, provisioning, replication, cutover and monitoring now all sit on the Arc resource in one portal view. | Steady |
| Failback | Reverse replication can be configured, with effort | Azure SQL MI Link supports failback for SQL Server 2022 and above — a genuinely differentiated capability, and directly relevant to the one-way-door problem from Session 2 | Promote/demote semantics; failback requires manual setup |
Sources: Microsoft — SQL migration to Azure VMs GA, July 2026 Accelerate SQL Server migration with Azure Arc GCP Database Migration Service docs
Layer 5 — Bulk data transfer, and the death of the appliance
| AWS | Azure | Google Cloud | |
|---|---|---|---|
| Online, managed | AWS DataSync — up to ~10 Gbps per task with an agent, ~5 Gbps without; integrity validation after every transfer; works with S3, EFS and the FSx family | Azure Storage Mover, AzCopy, Azure Data Factory | Storage Transfer Service — recommended for anything over 1 TB; scheduled and incremental transfers; resumes after errors; can now run over Cross-Cloud Interconnect rather than the public internet |
| Cloud-to-cloud | DataSync supports other clouds as a source | Storage Mover and Data Factory support S3 and GCS sources | Native S3 and Azure Blob sources, including event-driven transfers triggered by Azure Event Grid |
| Offline appliance | AWS Snowball Edge EOL 31 Dec 2026 210 TB Storage Optimized, existing customers only since 7 Nov 2025. Snowcone discontinued Nov 2024; Snowmobile retired early 2024. |
Azure Data Box family — Data Box Disk (~7–8 TB), Data Box (~80 TB usable), Data Box Heavy (~770 TB). current | Transfer Appliance — 100 TB and 480 TB models, recommended above 20 TB and up to about 1 PB. current |
| Physical upload site | AWS Data Transfer Terminal — bring your own drives to a secure facility and upload at up to 400 Gbps. Ten locations as of Feb 2026: New York, Los Angeles, San Francisco Bay Area, Seattle, Phoenix, London, Paris, Munich, Sydney, Tokyo. Billed per port-hour during your reservation. | — | — |
| Private link | AWS Direct Connect | Azure ExpressRoute | Cloud Interconnect — Dedicated at 10 or 100 Gbps, scaling to 8×10 Gbps or 2×100 Gbps per connection; Partner Interconnect from 50 Mbps to 10 Gbps |
Sources: AWS Snowball — end of support notice (page updated 27 Jul 2026) AWS Data Transfer Terminal — six new locations, Feb 2026 GCP Transfer Appliance guidance Azure Data Box
AWS is exiting the offline-appliance business. Snowmobile went in early 2024; Snowcone and the previous-generation Snowball devices in November 2024; the Snow Family closed to new customers on 7 November 2025; and AWS has now published an end-of-support notice for all Snowball devices in all commercial Regions on 31 December 2026, after which the Snow Console becomes inaccessible. AWS's stated reasoning is that improvements in network transfer made online migration faster and cheaper, and that customers strongly prefer it.
Which means the classic exam answer — "large dataset, poor connectivity, therefore Snowball" — is no longer correct on AWS. The current answers are DataSync over a Direct Connect link, or a Data Transfer Terminal if you can physically get drives to one of the ten cities. Azure and Google still sell appliances, so the appliance answer remains correct on those two. This is a real divergence between the three clouds, and it's about two weeks old at the level of the published notice.
Agents are the hidden cost, in two senses. Financially, replication agents and replication tasks bill continuously while they exist — including for source servers you cut over weeks ago and forgot to disconnect. Organisationally, installing an agent on several hundred production servers is a change-management project in its own right, which is why agentless discovery exists and why most programmes use agentless broadly with agents only where they need process-level attribution.
Free tool, expensive migration. Every VM-replication and containerisation tool here is free or free-for-a-window. That is not generosity; it's the correct commercial strategy for a switching cost. What you pay for is the staging storage during replication, the target compute from the moment you launch a test instance, the data transfer, and — on the source side — egress if you're coming from another cloud. Budget the migration, not the tool.
The 90/180-day clocks are per source server, and they start at first replication. A programme that begins replicating in wave 1 and cuts over in wave 6 can quietly exhaust a free window on machines that sat replicating for months. Start replication close to the wave that needs it.
Tool coverage is not uniform, and the gaps are where the schedule slips. Every vendor's tooling is strongest for the mainstream case — a Linux or Windows VM running a common runtime — and thins out fast at the edges: unusual operating systems, appliances you don't control, licence-bound software, and anything with a kernel module. Assume the last 10% of the estate needs a bespoke approach and price it separately, because it will consume a disproportionate share of the calendar.
▸ hybrid
Hybrid: three genuinely different philosophies
You've retained some applications on-premises. You still want one way of operating everything. Three companies have solved this and — unusually for cloud comparisons — they have solved it in three genuinely different ways, not three brands of the same thing.
AWS ships you their hardware. A rack arrives, it runs AWS services, you use the same APIs. Microsoft extends their control plane over your hardware. Nothing arrives; your existing servers get projected into Azure's management model and you govern them from there. Google gives you a Kubernetes fleet. Clusters anywhere — their cloud, your data centre, someone else's cloud — managed as one fleet with consistent config and policy.
Which is right depends entirely on what you're trying to make consistent: the infrastructure, the governance, or the workload.
| AWS | Azure | Google Cloud | |
|---|---|---|---|
| Flagship | AWS Outposts — AWS-managed racks and servers in your facility | Azure Arc — projects servers, Kubernetes clusters and databases into Azure Resource Manager | GKE Enterprise — fleet management, Config Sync, Policy Controller, Cloud Service Mesh |
| On-prem runtime | Outposts racks and servers; ECS Anywhere / EKS Anywhere for containers on your own hardware | Azure Local (formerly Azure Stack HCI); Azure Stack Hub for disconnected scenarios | Google Distributed Cloud — software-only on bare metal or VMware, plus connected and air-gapped appliance form factors |
| Manages other clouds? | No — AWS resources only | Yes — AWS and GCP resources can be Arc-enabled and governed from Azure. This is Arc's genuine differentiator. | Kubernetes only — GKE on AWS and Azure, plus attached clusters running any conformant Kubernetes distribution |
| Naming history you need to know | Stable | Azure Stack HCI → Azure Local | Anthos → GKE Enterprise (late 2023). Anthos on-prem became Google Distributed Cloud Virtual, then simply Google Distributed Cloud. Anthos clusters on bare metal became GDC software-only for bare metal. |
| Cost model | Capacity commitment — you buy hosts on a multi-year term, not instances | Per-resource for the paid Arc capabilities; basic inventory and organisation is free | Per-vCPU for the Enterprise tier; see the pricing note below |
| GPU / AI relevance | Outposts can host GPU-enabled instance types for on-prem inference while training runs in-Region | Arc-enabled Kubernetes gives you one policy and monitoring plane over GPU clusters wherever they are; Arc-enabled ML services extend the pattern | GKE fleet management over GPU node pools in multiple locations; the most natural fit if your serving stack is already Kubernetes |
Sources: Google Distributed Cloud software-only docs (naming history) GKE Enterprise release notes Azure Arc documentation
Your brief asked about "the current state of Anthos / GKE Enterprise," and the answer is that the rebrand went further than a rename. Anthos launched in 2019 as a paid subscription that unlocked features including GKE On-Prem. In late 2023 Google folded it into GKE Enterprise. Since around September 2025, "Anthos" no longer names a tier or edition at all — most former Anthos capabilities are now included in base GKE, and what remains chargeable is a set of optional SKUs layered on standard GKE: Cloud Service Mesh, multi-cloud cluster management, Backup for GKE, extended support, and multi-cluster Gateway.
The practical consequence is a billing trap worth knowing about: a single-region, single-cluster team that enabled the GKE Enterprise tier "because Enterprise sounded right" can be paying a per-vCPU management fee for capabilities they never use. If you inherit a GKE estate, check whether the Enterprise API is enabled and whether anything actually depends on it. Meanwhile, GCP guidance's GCP source mentions Anthos exactly twice in passing and treats it as current — it isn't.
Hybrid is a destination for some workloads and a phase for most. Applications you genuinely retained — sovereignty, physical dependency — are permanently hybrid, and a control plane is worth buying for them. But most hybrid connectivity exists because a migration is in progress, and that is a temporary condition you should be actively trying to end. A hybrid link that was supposed to last eighteen months and is still there at year five is a sign that the migration stalled — the infrastructure equivalent of the strangler fig that never finished.
Latency is the constraint nobody models. Splitting an application across the hybrid boundary is fine for an API edge and catastrophic for a chatty one, which is exactly the third component of data gravity from Session 1. A page render doing 200 database queries at 0.5 ms locally becomes 200 queries at 15 ms across a link — three seconds instead of a tenth of one. Measure the query count per request before you plan to split anything across the gap.
Consistency has three meanings and vendors conflate them. Consistent APIs (Outposts), consistent governance (Arc), and consistent runtime (GKE Enterprise) are different products solving different problems. Ask which one you actually need. Most enterprises discover they need governance consistency far more urgently than API consistency, which is a good part of why Arc has done as well as it has.
Hybrid is not multi-cloud, and multi-cloud is not portability. Running things on two clouds gives you redundancy and negotiating leverage and costs you a second set of everything. Actual portability requires designing for it — containers, open formats, no proprietary managed services in the critical path — and that design costs you the leverage of the managed services you didn't use. There is no free version of this trade-off; there is only being deliberate about which side you're on.
check
Worked on paper: what it costs and takes to move 10 TB
This is the calculation that settles more architecture arguments than any other in this stage, and it takes two minutes. The dataset: 10 TB of model weights and training data. The question: network or appliance, and what does it cost?
Step 1 — transfer time on three different links
Work in decimal units. 10 TB = 10,000 GB = 80,000 gigabits. The formula from Session 1:
days = TB ÷ (10.8 × Gbps × efficiency).
| Link | Effective throughput | Time for 10 TB | Verdict |
|---|---|---|---|
| 200 Mbps shared office line, 50% usable | 100 Mbps | 10 ÷ (10.8 × 0.2 × 0.5) = 9.3 days | Painful, and it saturates the link your business runs on |
| 1 Gbps dedicated, 70% efficiency | 700 Mbps | 10 ÷ (10.8 × 1 × 0.7) = 1.3 days | Fine. Start Friday evening, done by Sunday. |
| 10 Gbps interconnect, 70% efficiency | 7 Gbps | 10 ÷ (10.8 × 10 × 0.7) = 3.2 hours | A coffee break |
Step 2 — the appliance, and the crossover
An appliance's total elapsed time is not its transfer speed — the local copy is fast (Google quotes up to 40 Gbps onto a Transfer Appliance if your local network supports it). The elapsed time is the logistics: request the device, wait for shipping, load it, ship it back, wait for ingestion. Call it 7–10 days end to end, and Google's own guidance is that the appliance is for datasets above 20 TB or transfers that would take more than a week.
Appliance wins when: TB > 10.8 × Gbps × efficiency × (appliance round-trip days)
With a 10-day round trip and 70% efficiency, that's roughly 75 TB on a 1 Gbps link, 7.5 TB on a 100 Mbps link, and 750 TB on a 10 Gbps link.
So for our 10 TB: network wins on everything except the shared office line, and even there it's close. The appliance is the wrong tool here, and this is exactly the trap — "10 TB" sounds like a lot, so people reach for the appliance without doing the arithmetic. Ten terabytes is a weekend on a decent link.
Step 3 — the cost side, which is where clouds diverge
Ingress is free on all three clouds. Egress is not, and it is charged by whoever you are leaving. At mid-2026 first-tier internet rates:
| Moving 10 TB out of… | AWS | Azure | GCP Premium Tier |
|---|---|---|---|
| Per-GB rate | ~$0.09 | ~$0.087 | ~$0.12 |
| 10 TB total | ~$900 | ~$870 | ~$1,140–1,200 |
| Inter-region within the same cloud | Typically ~$0.02/GB — cheaper than internet egress but not free. Cross-AZ transfer is around $0.01/GB each way and is the charge most people forget entirely. | ||
| Hidden multipliers | NAT gateway data processing (~$0.045/GB on AWS) stacks on top of egress. A transfer routed through a NAT gateway costs roughly 50% more than the headline rate. Route bulk transfers through a gateway endpoint or a private link, not through NAT. | ||
Following the EU Data Act, all three hyperscalers introduced free egress for customers leaving the platform. Read the conditions, because they are narrower than the headlines: Google and Microsoft require you to terminate the account and complete the transfer within 60 days; AWS also uses a 60-day window but does not require you to close your account, though it applies additional scrutiny to repeat applications. None of these waivers apply to ordinary business egress — only to a genuine exit.
The bigger date is ahead: the EU Data Act bans cloud switching and egress charges outright from 12 January 2027, following a transition period that began in 2025. If you are modelling a cross-cloud migration for next year, that changes the economics materially, and it is worth checking the current state of implementation rather than assuming — this is exactly the sort of thing that shifts between when a page is written and when you read it.
Three cited examples worth knowing
- AWS is exiting offline data transfer — the most consequential news in this session.
The Snow Family closed to new customers on 7 November 2025, and AWS has published an end-of-support
notice for all Snowball devices in all commercial Regions on 31 December 2026, after which the Snow
Console becomes inaccessible. AWS's stated reasoning is that network transfer improvements made online
migration faster and cheaper and that customers strongly prefer it. The replacements are DataSync for
online transfer, the Data Transfer Terminal for physical drop-off, and Outposts for edge compute. This
is a genuine three-cloud divergence: Azure Data Box and Google Transfer Appliance remain fully
available.
aws.amazon.com/snowball — end of support notice, page updated 27 July 2026 - Data gravity is inverting the standard architecture for AI workloads. The
assumption that data moves to compute is breaking at AI scale — moving hundreds of terabytes across
clouds can cost comparably to the compute itself, sovereignty rules increasingly forbid it, and open
formats have made compute more portable than data. The emerging pattern is federated: keep raw data
where it is, move only model weights and aggregates, and place compute next to the data. One
published data-gravity calculator makes the economics concrete: at 50 TB, the gravity penalty against
AWS compute at $0.09/GB egress works out around 20%, against GCP at $0.12/GB around 34%, and against
a near-zero-egress GPU provider around 1%. Egress rate, not GPU hourly rate, dominates the placement
decision at that scale.
Data gravity calculator methodology, April 2026 VAST Data on multi-cloud data gravity, June 2026 - Wave planning and dependency mapping became console features this year. Azure
Migrate shipped Wave Planning in public preview alongside Arc-based discovery that can assess
Arc-enabled servers without deploying additional infrastructure, generating business cases within an
hour. AWS Transform's VMware agent orchestrates dependency mapping and intelligent wave planning as
part of the migration workflow. The practice you learned in Session 1 by hand is now partially
automated on two of the three clouds — which is a good reason to understand it by hand, because you
will be reviewing what the tool proposes rather than producing it.
What's new in Azure Migrate AWS Migration & Modernization announcements
your context
Apply it — moving your own model-serving stack
This is the shape of migration everything in this stage says is easy, and it's worth walking through to see exactly which layers of tooling you don't need. You need none of layers 1 through 4. No discovery — you know what you have. No VM replication — there is no VM, and even if there were, you would not want a block-level copy of it. No containerisation tool — it's already a container. No database migration — there is no database.
What's actually involved: build or re-tag the vLLM image into Artifact Registry; write a KServe
InferenceService or a plain Deployment plus Service; create a GPU node pool with the right
accelerator, taints and tolerations; set the autoscaler bounds you had on Modal; move the weights into a
GCS bucket in the same region as the node pool; and point the model path at it. The genuinely new work
is the parts Modal was doing for you invisibly — scale-to-zero behaviour, cold-start handling, and
request queueing — which now become explicit configuration. That's a replatform, and the
difficulty is in the platform semantics you inherit, not in moving anything.
Layer 5 is the only one that applies at all, and only for the weights — which, as established in Session 1, have fake gravity. If they're on Hugging Face, don't migrate them; re-pull them at the destination. Migration is for data you cannot re-derive.
Cross-cloud is where the abstractions stop being free, and the interesting part is that the container is still the easy bit. Walk the layers:
- The container is portable — GKE to EKS is a manifest translation, not a rebuild. The image runs unchanged.
- The GPU is a real decision. Your L4 and T4 experience maps onto different AWS instance families with different memory, different interconnect and different spot dynamics. Re-benchmark rather than assuming equivalence — throughput per dollar is not portable even when the code is.
- Identity does not port at all. GCP Workload Identity becomes IAM Roles for Service Accounts. Same concept, entirely different configuration, and this is reliably where a cross-cloud move actually stalls.
- The data has to move, and you pay on the way out. This is where the arithmetic above earns its keep. Your irreplaceable data — fine-tuning sets, eval suites with golden outputs, request telemetry — is probably single-digit terabytes, so at GCP's ~$0.12/GB you're looking at low hundreds of dollars and a few hours on a decent link. If you were exiting GCP entirely you might qualify for the exit-egress waiver; for a partial move you pay list.
- Spot/preemptible semantics differ. Your autoscaling 1–3 spot configuration has an AWS equivalent, but preemption notice periods and reclaim behaviour are not the same, and inference services care about that.
The honest summary: a week of work, most of it identity and benchmarking, none of it data migration in the sense this session has been describing. Compare that with LEDGER-CORE's thirty-week single-slice strangle in Session 2 and you have the clearest possible statement of what cloud-native architecture actually buys you. It is not performance and it is not cost — it is optionality, and migration is when you cash it in.
Optional hands-on lab — run one real migration end to end
One lab, one cloud, smallest possible everything. The goal is to feel the replication-and-cutover loop once, because reading about a test launch is not the same as doing one.
Migrate one tiny VM with a replication service
Create a single minimal VM in cloud A — smallest burstable SKU, small disk, a static web page on it so you can tell it's the right machine. Then use cloud B's VM-replication service to migrate it: install the agent, watch replication complete, launch a test instance into an isolated subnet, verify the page, then cut over.
What you're there to observe: that the test launch costs nothing and can be repeated, and that the source keeps running the whole time. That property is the entire reason this class of tool exists.
Calibrate the transfer formula against reality
Generate a few GB of files. Move them to a bucket with the managed transfer service rather than a plain CLI copy — you want to see the job semantics: scheduling, incremental re-runs, integrity validation, and resumption after you deliberately kill it mid-transfer.
Then compute your achieved throughput, plug it into the crossover formula as your real efficiency factor, and work out where your appliance crossover sits. That number is more useful to you than any published benchmark.
- Budget alert before anything else. A budget with an alert at a low threshold — single-digit dollars. Free on all three clouds, two minutes, non-negotiable for this lab specifically because replication tasks bill continuously and silently.
- Smallest SKU, free tier, no GPUs. Nothing here needs a GPU. A burstable micro/small instance with the minimum disk is enough. Stay in one region — cross-region transfer bills even between your own resources.
- Know what bills while you sleep: the replication agent and its staging storage; the staging-area volumes and any snapshots they leave behind; the target instance from the moment a test launch starts, not from cutover; any load balancer or public IP address you created; and data transfer out if you download anything back.
- Teardown, in this exact order — the order matters:
① Disconnect and delete the replication task or source server entry first, and uninstall the agent from the source. Deleting the target instance while replication is still configured leaves the task running and billing.
② Terminate every target instance, including test-launch instances — these are easy to miss because they live in a separate isolated subnet you stopped looking at.
③ Delete the staging-area volumes and snapshots. Replication services create these automatically and frequently do not remove them.
④ Delete load balancers, public IPs and NAT gateways. These bill hourly regardless of traffic and outlive the instance that prompted them.
⑤ Delete the source VM and the buckets.
⑥ Check the bill 48 hours later, not the console. The console shows what exists; the bill shows what's charging. They disagree more often than they should. - The specific trap in this lab: migration services are designed to be resilient and to keep trying. That is exactly the wrong property in a teardown. Remove the replication configuration before you remove anything it points at.
close
What you can now do
Trace the arc backwards and the shape of the stage is clear. You can look at an application and choose an R with reasons you can defend, and you know that the choice composes over time rather than being permanent. You can take a portfolio, map its dependencies, distinguish an API edge from a shared schema, and sequence it into waves where the heaviest dataset sets the pace and the first wave is a rehearsal. You can take a monolith you're not allowed to stop and shrink it one slice at a time with a facade, an anti-corruption layer, and a reconciliation gate before the one-way door. You can do the arithmetic on transfer time, egress cost, and the appliance crossover in your head. You know what AWS, Azure and Google actually sell at each of the five tooling layers, which three product names in the materials are dead, and that AWS is leaving the appliance business at the end of this year.
And you can explain — precisely, not vaguely — why your own serving stack is a week of work while LEDGER-CORE is thirty weeks per slice. Statelessness, externalised state, and containerisation are usually sold as operational properties. They are also, and perhaps more importantly, the properties that determine whether you are ever able to leave.
Every architectural decision you make from here has a migration cost attached to it, payable later, by someone who may be you. The managed service that saves you three weeks now is the thing that pins you in place in four years. That's not an argument against using managed services — it's an argument for knowing which ones you've chosen deliberately and which ones you drifted into. Portability is not a property you add at migration time. It is a property you either preserved or spent.
complete
Commands available: "re-teach tab X" "go deeper on X" "more visual in tab X" "compare harder in tab X" "ground tab X" "fix file". Each returns the full updated file with all other tabs preserved.
Highest-value next moves for this stage specifically: "compare harder in tab 3" to expand the five comparison tables with more rows, or "go deeper on dual-write" — the data half of S2 is the part that most often gets underestimated in a real programme.