Lesson 10 / 10
Cloud Migration Strategies
Comparing rehost, replatform, and refactor across the same ten-item set: the decision handed over, migration days, the provider-specific item that makes exit harder, the wave count and how long the two systems run together, the annual load of the item left behind, and the cost of a dual system that never ends.
Published Updated
In this lesson
- Compares three migration strategies against the same item set in terms of the decision handed over, migration days, and the provider-specific item
- Counts staging: the service moved per wave, how long the two systems run together, and the item left behind
- Measures the cost of not moving and computes the annual load of the case where the two systems never stop running together
- Derives the item each strategy fails to win and shows that a per-item choice beats uniform strategies
Contents
The previous lesson measured the terms under which capacity is rented and found that three different models won across three load profiles. All of those calculations were made under one silent assumption: that the work to be moved is already rented. But the fictional regional measurement network runs today in its own room.
Three paths are measured here. Rehost moves the item as-is onto a rented machine; the code does not change. Replatform replaces some of the item’s parts with managed counterparts; the interface layer is rewritten. Refactor rewrites the item from scratch around rented building blocks. The common migration frameworks also count repurchasing an item as a bought service, retiring it, or leaving it where it is; this lesson stays with the three because it compares three depths of moving the same item. The wave mechanics of a staged rollout and the pattern of wrapping the old system piece by piece were measured in earlier courses; what is measured here is not the wave itself but the annual cost of the case where the waves never finish.
The Item Set and Three Strategies
- CG25. The item set is 10 services; each item’s size (1–3), annual operating days, and annual change count are fictional.
- CG26. Strategy profiles are fixed per item and written in the model file: the decisions handed over are 3, 7, and 11, respectively.
- CG27. The decision horizon is 5 years; an item whose five-year net gain is not above zero is not moved.
- CG28. The team processes 20 migration days per wave; a wave takes 30 calendar days.
- CG29. As long as the two systems run together, 1.5 extra operating days accrue per month. If any item is left behind, this cost never ends.
- CG30. On the mixed path, each item gets the strategy that gives the highest five-year net.
// migration/model.mjs — migration is a MODEL: the fictional measurement network's ten items and three // strategy profiles. There is no real migration tool, provider, or calendar; every number is fictional. // name, size (1-3), annual operating days, annual change count export const ITEM = [ ["read-collector", 3, 22, 30], ["validator", 2, 14, 18], ["billing", 3, 18, 12], ["work-order-service", 2, 10, 9], ["subscriber-portal", 2, 12, 24], ["nightly-batch-job", 1, 8, 4], ["read-store", 3, 20, 3], ["tariff-store", 1, 6, 2], ["report-endpoint", 1, 3, 1], ["identity-directory", 1, 5, 2], ]; // handoff: decisions handed over per item; drop: share dropped from operating load; delivery: days gained // per change; days: migration days per unit size; specific: number of provider-specific items per item export const STRATEGY = { rehost: { handoff: 3, drop: 0.15, delivery: 0.05, days: 2, specific: 1, rewrite: "unchanged" }, replatform: { handoff: 7, drop: 0.55, delivery: 0.20, days: 9, specific: 3, rewrite: "partial" }, refactor: { handoff: 11, drop: 0.80, delivery: 0.70, days: 34, specific: 5, rewrite: "full" }, }; export const NAME = Object.keys(STRATEGY); export const HORIZON = 5, WAVE_CAP = 20, WAVE_DAYS = 30, DUAL_MONTH = 1.5; // fictional export const migrationDays = (k, s) => STRATEGY[s].days * k[1]; export const gain = (k, s) => k[2] * STRATEGY[s].drop + k[3] * STRATEGY[s].delivery; export const net = (k, s) => gain(k, s) * HORIZON - migrationDays(k, s); export const best = (k) => NAME.reduce((a, s) => (net(k, s) > net(k, a) ? s : a), NAME[0]); // One path: one strategy per item. An item whose net gain stays below zero is not moved. export function run(pick) { const moved = ITEM.filter((k) => net(k, pick(k)) > 0); const left = ITEM.filter((k) => net(k, pick(k)) <= 0); const sum = (f) => moved.reduce((a, k) => a + f(k), 0); const days = sum((k) => migrationDays(k, pick(k))); const waves = Math.ceil(days / WAVE_CAP), together = waves * WAVE_DAYS; const transition = (together / 30) * DUAL_MONTH; const persistent = left.length ? DUAL_MONTH * 12 : 0; const annual = sum((k) => gain(k, pick(k))) - persistent; return { moved: moved.length, left: left.length, days, waves, together, transition, persistent, annual, handoff: sum((k) => STRATEGY[pick(k)].handoff), specific: sum((k) => STRATEGY[pick(k)].specific), remainingLoad: sum((k) => k[2] * (1 - STRATEGY[pick(k)].drop)) + left.reduce((a, k) => a + k[2], 0), leftLoad: left.reduce((a, k) => a + k[2], 0), net5: annual * HORIZON - days - transition, }; }
Three Strategies on the Same Set
// migration/strategy.mjs — three strategies on the same item set: decisions handed over, // item rewritten, migration days, and the provider-specific item that makes exit harder import { ITEM, STRATEGY, NAME, HORIZON, run, net, best } from "./model.mjs"; const s = (x, n) => String(x).padStart(n); const y = (x, n = 1) => x.toFixed(n); const SHORT = { rehost: "rehost", replatform: "replatform", refactor: "refactor" }; const R = Object.fromEntries(NAME.map((a) => [a, run(() => a)])); R.mixed = run(best); const PATH = [...NAME, "mixed"]; console.log(`${ITEM.length} items, decision horizon ${HORIZON} years; every number is FICTIONAL ` + `and in "operating days" and "migration days".`); console.log(); console.log("measure | rehost | replatform | refactor | mixed "); console.log("----------------------------------|-------------|--------------|------------|--------"); for (const [name, f] of [ ["items moved", (r) => `${r.moved}/${ITEM.length}`], ["decisions handed over", (r) => r.handoff], ["item rewritten", (r, a) => (a === "mixed" ? "mixed" : STRATEGY[a].rewrite)], ["migration days (total)", (r) => r.days], ["provider-specific items (exit)", (r) => r.specific], ["annual gain (operating days)", (r) => y(r.annual)], ["remaining annual operating load", (r) => y(r.remainingLoad)]]) console.log(`${name.padEnd(33)} | ${s(f(R[PATH[0]], PATH[0]), 11)} | ${s(f(R[PATH[1]], PATH[1]), 12)} | ` + `${s(f(R[PATH[2]], PATH[2]), 10)} | ${s(f(R.mixed, "mixed"), 7)}`); console.log(); console.log(`${HORIZON}-year net gain per item (operating days; the highest is chosen)`); console.log("item | size | load | change | rehost | replatform | refactor | choice "); console.log("---------------------|----------|--------|----------|------------|--------------|------------|-------------"); for (const k of ITEM) { const n = NAME.map((a) => net(k, a)); console.log(`${k[0].padEnd(20)} | ${s(k[1], 8)} | ${s(k[2], 6)} | ${s(k[3], 8)} | ` + `${s(y(n[0]), 10)} | ${s(y(n[1]), 12)} | ${s(y(n[2]), 10)} | ${SHORT[best(k)]}`); } const count = (a) => ITEM.filter((k) => best(k) === a).length; console.log(`choice distribution: ` + NAME.map((a) => `${a} ${count(a)}`).join(", ") + `; all three strategies win on at least one item.`);
10 items, decision horizon 5 years; every number is FICTIONAL and in "operating days" and "migration days". measure | rehost | replatform | refactor | mixed ----------------------------------|-------------|--------------|------------|-------- items moved | 10/10 | 10/10 | 6/10 | 10/10 decisions handed over | 30 | 70 | 66 | 78 item rewritten | unchanged | partial | full | mixed migration days (total) | 38 | 171 | 442 | 339 provider-specific items (exit) | 10 | 30 | 30 | 34 annual gain (operating days) | 22.9 | 85.9 | 117.1 | 132.6 remaining annual operating load | 100.3 | 53.1 | 50.8 | 42.3 5-year net gain per item (operating days; the highest is chosen) item | size | load | change | rehost | replatform | refactor | choice ---------------------|----------|--------|----------|------------|--------------|------------|------------- read-collector | 3 | 22 | 30 | 18.0 | 63.5 | 91.0 | refactor validator | 2 | 14 | 18 | 11.0 | 38.5 | 51.0 | refactor billing | 3 | 18 | 12 | 10.5 | 34.5 | 12.0 | replatform work-order-service | 2 | 10 | 9 | 5.8 | 18.5 | 3.5 | replatform subscriber-portal | 2 | 12 | 24 | 11.0 | 39.0 | 64.0 | refactor nightly-batch-job | 1 | 8 | 4 | 5.0 | 17.0 | 12.0 | replatform read-store | 3 | 20 | 3 | 9.8 | 31.0 | -11.5 | replatform tariff-store | 1 | 6 | 2 | 3.0 | 9.5 | -3.0 | replatform report-endpoint | 1 | 3 | 1 | 0.5 | 0.3 | -18.5 | rehost identity-directory | 1 | 5 | 2 | 2.3 | 6.8 | -7.0 | replatform choice distribution: rehost 1, replatform 6, refactor 3; all three strategies win on at least one item.
The decisions handed over are 30, 70, and 66. Refactor’s 66 is not a saving but a shortfall: it hands over 11 decisions per item, yet can only move six of the ten items. Migration days climb from 38 to 171 and to 442, and the provider-specific items that make exit harder rise from 10 to 30. Rehost gains the least (22.9 operating days annually) but is the path that harms exit the least.
The second table shows where this gain comes from. What the three items refactor wins have in common is not load but change count: 30, 18, and 24. The read store, which changes only 3 times a year, is the second-heaviest item by load (20 annual days), yet nets -11.5 under refactor; the payoff of rewriting comes not from operating load but from cheaper changes. The report endpoint is the extreme case: size 1, load 3, change 1, and -18.5 under refactor — on a small, static service, refactor never pays back within five years; the best choice there is rehost, at 0.5. The choice distribution is 1, 6, and 3: all three strategies win on at least one item.
Staging and the Cost of Not Moving
// migration/wave.mjs — staging and the cost of not moving: service per wave, how long the // two systems run together, the annual load left behind, and the dual system that never ends import { NAME, WAVE_CAP, WAVE_DAYS, DUAL_MONTH, run, best } from "./model.mjs"; const s = (x, n) => String(x).padStart(n); const y = (x, n = 1) => x.toFixed(n); const R = { ...Object.fromEntries(NAME.map((a) => [a, run(() => a)])), mixed: run(best) }; const PATH = [["rehost", NAME[0]], ["replatform", NAME[1]], ["refactor", NAME[2]], ["mixed", "mixed"]]; console.log(`wave capacity ${WAVE_CAP} migration days, wave duration ${WAVE_DAYS} calendar days; ` + `as long as the two systems run together, ${DUAL_MONTH} extra operating days accrue per month (fictional).`); console.log(); console.log("path | migration days | waves | items/wave | together | left | dual system | 5-year net "); console.log("-------------|------------------|---------|--------------|------------|--------|---------------|-------------"); for (const [name, v] of PATH) { const r = R[v]; console.log(`${name.padEnd(12)} | ${s(r.days, 16)} | ${s(r.waves, 7)} | ${s(y(r.moved / r.waves), 12)} | ` + `${s(`${r.together} d`, 10)} | ${s(r.left, 6)} | ` + `${s(r.persistent ? `${y(r.persistent)}/yr` : "ends", 13)} | ${s(y(r.net5), 12)}`); } const m = R[NAME[2]]; console.log(`transition cost (one-time): ` + PATH.map(([a, v]) => `${a} ${y(R[v].transition)}`).join(", ") + "."); console.log(`refactor leaves ${m.left} items behind; their annual load of ${y(m.leftLoad)} days ` + `and the dual system at ${y(m.persistent)} days/yr never end.`);
wave capacity 20 migration days, wave duration 30 calendar days; as long as the two systems run together, 1.5 extra operating days accrue per month (fictional). path | migration days | waves | items/wave | together | left | dual system | 5-year net -------------|------------------|---------|--------------|------------|--------|---------------|------------- rehost | 38 | 2 | 5.0 | 60 d | 0 | ends | 73.7 replatform | 171 | 9 | 1.1 | 270 d | 0 | ends | 245.0 refactor | 442 | 23 | 0.3 | 690 d | 4 | 18.0/yr | 109.0 mixed | 339 | 17 | 0.6 | 510 d | 0 | ends | 298.3 transition cost (one-time): rehost 3.0, replatform 13.5, refactor 34.5, mixed 25.5. refactor leaves 4 items behind; their annual load of 34.0 days and the dual system at 18.0 days/yr never end.
Staging converts the wave count into a calendar. Rehost finishes in 2 waves and moves 5.0 services per wave; replatform falls to 1.1, and refactor to 0.3 services — most waves cannot even finish a single service. The two systems run together for 60, 270, and 690 days, and the transition cost that accrues over that time is 3.0, 13.5, and 34.5 operating days.
The real item is in the last two columns. For rehost and replatform, the dual system ends; for refactor it does not, because four items are never moved. Those four items’ annual operating load of 34.0 days stays as it is, and on top of it, the dual system adds 18.0 days/year permanently. Not moving is not a saving; it is an unending subscription: the path that is second in annual gain (117.1) drops to third in the five-year net, at 109.0, while replatform posts 245.0.
The mixed path beats them all: 339 migration days, 17 waves, 0 left behind, and a five-year net of 298.3 — the cost of applying a single strategy to the whole set is 53.3 operating days relative to the best uniform path.
Summary
- The three strategies hand over 30, 70, and 66 decisions across the same ten items; migration days are 38, 171, and 442, and provider-specific items are 10, 30, and 30. Refactor is second in annual gain (117.1 days) but third in the five-year net (109.0 against 245.0).
- The payoff of rewriting comes from change count, not operating load: the three items it wins have annual changes of 30, 18, and 24, while the read store, which changes only 3 times a year, nets -11.5. It posts -18.5 on the small, static report endpoint; the best choice there is rehost (0.5).
- Staging moves 5.0, 1.1, and 0.3 services per wave; the two systems run together for 60, 270, and 690 days, and the transition cost is 3.0, 13.5, and 34.5 operating days.
- Not moving is an unending cost: the 34.0-day annual load of the four items left behind does not shrink, and the dual system does not end at 18.0 days/year. The mixed path, choosing per item, leaves nothing behind and posts 298.3 in the five-year net.
Course Wrap-Up
| Lesson | Decision Handed Over | Received in Exchange | Cannot Carry at Exit |
|---|---|---|---|
| What Is Cloud Computing | 3 decisions (purchase, quantity, placement) | wait 30.0 → 1.0 days | 4 of 8 items |
| Service Models | 5 / 23 / 25 of 26 | touch 91 → 82 / 19 / 4 | embedded 2 / 8 / 17 decisions |
| Deployment Models | 16/24 of the checks | elasticity window 21.0 → 1.0 days | 16 check items, 6 boundary bindings |
| Comparison with On-Premises | ownership, 8-unit upfront purchase | hidden share 64% → 13% | 250 fu exit fee |
| Shared Responsibility Model | 6 / 16 / 20 of 24 | left with the customer 18 → 8 → 4 | negotiable 6 / 16 / 20 items |
| Regions and Availability Zones | placement of 6 of 13 items | 0 extra capacity cost | distribution definition of 6 items |
| Edge Locations | 3 decisions (location, routing, eviction) | latency drop 21.0% | warmed-up cache, +30.5% load |
| The Pillars of the Design Framework | 8 of 12 decisions | operations +0.63, security +1.00 | 6 of 8 handoffs (43.75%) |
| Pricing Models | 0 / 1 / 2 decisions | on-demand 61.3% more expensive in the flat profile | remaining commitment 2,083.20 units |
| Cloud Migration Strategies | 30 / 70 / 66 decisions | annual 22.9 / 85.9 / 117.1 days | provider-specific 10 / 30 / 30 |
The course’s rule is the same across all ten rows: leasing is handing over a decision; every decision handed over carries a gain and an exit cost. Without the middle column, the left column is a list of losses; without the right column, the middle column is a sales pitch.
The last column, across ten rows, comes down to four families. A decision embedded in the service recurs across four lessons (service models, shared responsibility, pillars, migration strategies): the item moves, the decision that configures it does not. A distribution operated by the provider and never visible appears in two lessons (deployment models, regions): something that cannot be tested has no definition to carry either. State accumulated over time appears in two lessons (performance history, warmed-up cache). The remaining liability the contract writes down appears in two lessons (exit fee, remaining commitment) and, of the four, is the only family that is visible up front. The remaining three are discovered only at the moment of exit, unless they are measured beforehand.
The decision to lease was made, its depth and pattern were chosen, the responsibility boundary was drawn, its geography and price were set, and the path of moving it was measured. Yet the most concrete thing being rented was never taken up in these ten lessons: the machine itself, and the network that connects those machines to one another. Capacity was always counted as an abstract “unit,” the network always as a “latency.” M23/K02 Compute and Networking in the Cloud opens up those two abstractions: what the difference between rented machine types is measured by, what items a network is built from, and how many decisions the path between two machines carries.
To keep your progress and take notes, Log in
My notes
Log in to take notes.