Lesson 21 / 21
Vendor Lock-In
The twelve rented items split into portability degrees, and it turns out bill weight never measures portability: 48.3% of the monthly bill comes from two items portable through a standard interface, 9.6% from three items that cannot be moved at all. The exit bill is 879 fictional units, 52% of it request fee rather than bytes; yet that is only 9.1% of the move cost, the rest is 257 person-days of work. The three paths to reducing lock-in are measured by prep, move, and capability lost: the lowest break-even point is 48.9%, and one path's break-even is 466% — it does not pay off even if the move is certain.
Published Updated
In this lesson
- Splits rented items into four portability degrees: counts item count, workload, bill share, and what replaces the ones that cannot be moved
- Compares three ways to reduce lock-in by prep cost, capability lost, and break-even move probability
Contents
The previous lesson folded limits into the scaling plan, and the same thing stood in every row: the rented side sets both the limit’s value and whether it can be increased. This lesson’s question sits at the end of that sentence — how possible is going somewhere else, and what it costs.
Items, Degrees, and the Exit Bill
The example is the fictional regional measurement network: five teams, three environments, a
rented machine, network, store, function, and edge layer. Items and amounts are fictional; the
currency is fictional too and is called unit.
CC31: twelve items split into four portability degrees — moved through a standard interface, moved with adaptation, requiring a rewrite, non-portable. CC32: every item has a move workload; on non-portable items this number is the workload of what gets set up in its place. CC33: every item uses a set of provider-specific capabilities, and prep costs some of them. CC34: the exit fee is a one-time charge made of two parts — bytes and requests. CC35: the horizon is 36 months; prep is paid across that span, the move only if the move actually happens.
The items and paths are a process-internal model.
// cost/portability.mjs — portability model of the rented items: no real inventory. Every item // is tied to a *portability degree*, a move workload, and a count of provider-specific // capabilities. The currency is fictional and is called `unit`; unit prices are taken from the // fictional list in this topic's first lesson. export const PERSON_DAY = 34; // fictional cost of one person-day (unit) export const EGRESS_GB = 0.082, READ = 0.36; // per-GB egress, per-million-requests read export const HORIZON = 36; // evaluation horizon (months) // name, degree, work (person-days), monthly unit, GB, objects (millions), special capability, replaced by export const ITEM = [ ["raw measurement store", "standard interface", 4, 6.5, 308.6, 1244.16, 0, "-"], ["rented machine", "standard interface", 6, 535.7, 0, 0, 0, "-"], ["snapshot", "adaptation", 9, 102.7, 2140, 0.4, 2, "-"], ["record store", "adaptation", 7, 23.1, 1360, 8, 1, "-"], ["private connectivity", "adaptation", 9, 46.0, 0, 0, 3, "-"], ["managed array store", "adaptation", 18, 196.0, 640, 3, 4, "-"], ["event queue", "rewrite", 22, 58.0, 90, 6, 5, "-"], ["function execution", "rewrite", 34, 47.2, 0, 0, 6, "-"], ["identity and policy", "rewrite", 41, 0, 0, 0, 7, "-"], ["key management", "non-portable", 38, 34.0, 0, 0, 3, "own key store"], ["audit log pipeline", "non-portable", 24, 23.1, 620, 5, 2, "own log pipeline"], ["threat detection", "non-portable", 45, 51.0, 0, 0, 4, "rule writing and maintenance"], ]; export const DEGREE = ["standard interface", "adaptation", "rewrite", "non-portable"]; // paths to reduce lock-in. Multipliers apply to the four degrees in order: with the prep done, how much of // the move workload remains. The exit multiplier drops if data no longer sits with the other provider. // name, one-time person-days, monthly person-days, monthly extra units, multipliers, exit, capability lost export const PATH = [ ["nothing", 0, 0.0, 0.0, [1.00, 1.00, 1.00, 1.00], 1.00, 0.00], ["abstraction layer", 25, 0.6, 0.0, [1.00, 0.25, 0.35, 1.00], 1.00, 0.45], ["run on two providers", 45, 0.0, 512.3, [0.00, 0.00, 0.50, 1.00], 0.05, 0.75], ["common-feature only", 0, 3.2, 0.0, [0.60, 0.30, 0.35, 0.00], 1.00, 0.85], ]; export const sum = (v) => v.reduce((a, b) => a + b, 0); export const bucket = (d) => ITEM.filter((k) => k[1] === d);
// cost/exit.mjs — items are split by portability degree, the exit bill is totaled, and the three // paths to reduce lock-in are measured by prep, move, and the *capability lost*; move probability // is scanned to work out each path's break-even point. Numbers come from the model alone. import { ITEM, DEGREE, PATH, PERSON_DAY, EGRESS_GB, READ, HORIZON, sum, bucket } from "./portability.mjs"; const write = (w, ...s) => console.log(s.map((v, i) => (w[i] < 0 ? String(v).padEnd(-w[i]) : String(v).padStart(w[i]))).join("")); const t0 = process.hrtime.bigint(); const MONTHLY = sum(ITEM.map((k) => k[3])), WORK = sum(ITEM.map((k) => k[2])); const GB = sum(ITEM.map((k) => k[4])), OBJ = sum(ITEM.map((k) => k[5])); const CAP = sum(ITEM.map((k) => k[6])), EXIT = GB * EGRESS_GB + OBJ * READ; console.log(`${ITEM.length} rented items, monthly ${MONTHLY.toFixed(1)} units (fictional currency)`); console.log("\n1. portability degree, bill weight, and exit bill"); const A = [-20, 7, 6, 15, 12, 12, 8, 14, 12]; write(A, "degree", "items", "work", "monthly units", "bill share", "capability", "GB", "objects (mn)", "exit units"); for (const d of DEGREE) { const g = bucket(d), br = sum(g.map((k) => k[3])); const gb = sum(g.map((k) => k[4])), n = sum(g.map((k) => k[5])); write(A, d, g.length, sum(g.map((k) => k[2])), br.toFixed(1), `${(100 * br / MONTHLY).toFixed(1)}%`, sum(g.map((k) => k[6])), gb.toFixed(0), n.toFixed(2), (gb * EGRESS_GB + n * READ).toFixed(1)); } write(A, "total", ITEM.length, WORK, MONTHLY.toFixed(1), "100%", CAP, GB.toFixed(0), OBJ.toFixed(2), EXIT.toFixed(1)); console.log(" where the non-portable items are replaced: " + bucket("non-portable").map((k) => k[7]).join(", ")); const raw = ITEM[0]; console.log(` ${(100 * OBJ * READ / EXIT).toFixed(0)}% of the exit bill is request fee, not bytes: raw measurement`); console.log(` store drops ${(raw[4] * EGRESS_GB).toFixed(1)} units of bytes against ` + `${(raw[5] * READ).toFixed(1)} units of requests`); console.log(` exit ${EXIT.toFixed(0)} units, ${(100 * EXIT / MONTHLY).toFixed(0)}% of the monthly bill; ` + `work ${(WORK * PERSON_DAY).toFixed(0)} units`); console.log(` exit is ${(100 * EXIT / (EXIT + WORK * PERSON_DAY)).toFixed(1)}% of the ` + `total move cost`); const measure = (y) => { const [, oneTime, monthlyDays, monthlyUnit, mult, exitMult, lost] = y; const work = sum(DEGREE.map((d, i) => sum(bucket(d).map((k) => k[2])) * mult[i])); return { prep: oneTime * PERSON_DAY + HORIZON * (monthlyDays * PERSON_DAY + monthlyUnit), work, move: work * PERSON_DAY + EXIT * exitMult, lost: CAP * lost }; }; console.log("\n2. three paths to reduce lock-in (36-month horizon)"); const C = [-23, 13, 16, 12, 12, 12]; write(C, "path", "prep units", "remaining work", "move units", "capability", "break-even"); const base = measure(PATH[0]); for (const y of PATH) { const o = measure(y), p = y === PATH[0] ? null : o.prep / (base.move - o.move); write(C, y[0], o.prep.toFixed(0), o.work.toFixed(1), o.move.toFixed(0), o.lost ? `-${o.lost.toFixed(0)}` : "0", p === null ? "-" : `${(100 * p).toFixed(1)}%`); } console.log(` ${CAP} special capabilities exist; every path buys its move cost with capability`); console.log("\n3. scanning move probability: expected cost and the cheapest path"); const D = [13, 12, 20, 23, 22, 23]; write(D, "probability", ...PATH.map((y) => y[0]), "cheapest"); for (const p of [0.05, 0.25, 0.50, 0.70, 1.00]) { const v = PATH.map((y) => { const o = measure(y); return o.prep + p * o.move; }); write(D, `${(100 * p).toFixed(0)}%`, ...v.map((x) => x.toFixed(0)), PATH[v.indexOf(Math.min(...v))][0]); } const ps = PATH.slice(1).map((y) => [y[0], measure(y).prep / (base.move - measure(y).move)]); const best = ps.reduce((a, b) => (a[1] <= b[1] ? a : b)); console.log(` lowest break-even ${best[0]}: ${(100 * best[1]).toFixed(1)}%; below it no ` + `prep pays off`); console.log(` ${ps.filter((x) => x[1] > 1).length} path's break-even is above 100%: it does not ` + `pay off even if the move is certain`); const ms = Number(process.hrtime.bigint() - t0) / 1e6; // raw measurement is not printed, only the band is checked console.log(`\nthree tables within the 2000 ms band: ${ms < 2000 ? "yes" : "no"}`);
12 rented items, monthly 1123.3 units (fictional currency)
1. portability degree, bill weight, and exit bill
degree items work monthly units bill share capability GB objects (mn) exit units
standard interface 2 10 542.2 48.3% 0 309 1244.16 473.2
adaptation 4 43 367.8 32.7% 10 4140 11.40 343.6
rewrite 3 97 105.2 9.4% 18 90 6.00 9.5
non-portable 3 107 108.1 9.6% 9 620 5.00 52.6
total 12 257 1123.3 100% 37 5159 1266.56 879.0
where the non-portable items are replaced: own key store, own log pipeline, rule writing and maintenance
52% of the exit bill is request fee, not bytes: raw measurement
store drops 25.3 units of bytes against 447.9 units of requests
exit 879 units, 78% of the monthly bill; work 8738 units
exit is 9.1% of the total move cost
2. three paths to reduce lock-in (36-month horizon)
path prep units remaining work move units capability break-even
nothing 0 257.0 9617 0 -
abstraction layer 1584 161.7 6377 -17 48.9%
run on two providers 19973 155.5 5331 -28 466.0%
common-feature only 3917 52.8 2676 -31 56.4%
37 special capabilities exist; every path buys its move cost with capability
3. scanning move probability: expected cost and the cheapest path
probability nothing abstraction layer run on two providers common-feature only cheapest
5% 481 1903 20239 4051 nothing
25% 2404 3179 21306 4586 nothing
50% 4808 4773 22638 5255 abstraction layer
70% 6732 6048 23704 5790 common-feature only
100% 9617 7961 25304 6593 common-feature only
lowest break-even abstraction layer: 48.9%; below it no prep pays off
1 path's break-even is above 100%: it does not pay off even if the move is certain
three tables within the 2000 ms band: yes
The numbers belong to the measurement class; their inputs are the assumptions above.
Bill Weight Does Not Measure Portability
The first table lines up two rankings, and they run opposite. 48.3% of the bill comes from the two standard-interface items, whose workload is 10 person-days. The three items requiring a rewrite hold 9.4% of the bill but 97 person-days; the three non-portable ones hold 9.6% and demand 107 person-days.
The same inverse relationship shows up in the capability column: the two items holding half the bill use zero provider-specific capabilities, the seven items holding 19% use all 27. Lock-in accumulates not with money spent but with the feature used.
What replaces the three non-portable items is written into their names: an own key store, an own log pipeline, rule writing — not a service in any of the three, but ongoing work.
Where the Exit Bill Accumulates
The exit bill is 879 units — 78% of the monthly bill, paid once. 52% of it is request fee, not bytes, and one item alone produces the difference: raw measurement store carries 25.3 units of bytes against 447.9 units of requests. The splitting decision from this topic’s first lesson is paid a second time here.
But exit is not the move’s biggest item: 257 person-days of work run 8,738 units, and exit is only 9.1% of the total. Exit shows up on a single line; the real item never shows up on any bill at all.
The Price of Prep Is Capability
The second table measures three ways to reduce lock-in. The abstraction layer costs 1,584 units over 36 months, drops the move from 9,617 to 6,377 units, and costs 17 of 37 capabilities. Running on two providers at once drops the move to 5,331 units and all but zeroes the exit, but its prep is 19,973 units — twice the price of the move itself. Using only common features gives the cheapest move (2,676 units), prep of 3,917, and takes 31 of 37 capabilities.
What the three paths share sits in that last column: none of them buys portability with money, all of them trade it for capability. None touches the three non-portable items.
Where Lock-In Is Acceptable
The third table scans move probability. At 5% and 25%, the cheapest path is doing nothing; at 50% the abstraction layer takes over, at 70% and above the common-feature path leads. The lowest break-even is 48.9%: if the three-year move probability sits below that, no prep pays for itself.
Running on two providers breaks even at 466%; being meaningless as a probability is the result itself. Because a second three-year bill costs more than a one-time move, that path does not pay off even if the move is certain.
In this table, lock-in is not a mistake, it is a price; the measure is not “are we locked in” but “is our move probability above 48.9%.”
Summary
- Bill weight does not measure portability: 48.3% of the bill comes from the two items that move through a standard interface, and those two use zero special capabilities; seven items holding 19% use 27 capabilities.
- Three items never move at all; what replaces them is not a service but ongoing work (107 person-days).
- The exit bill is 879 units, and 52% of it is request fee — seventeen times the bytes in raw measurement store. Still, it is only 9.1% of the move; 257 person-days of work at 8,738 units is the real item, and it never shows up on any bill.
- All three paths trade portability for capability (17, 28, 31 capabilities), and none touches the three non-portable items. The lowest break-even is 48.9%, the highest 466%.
Course Wrap-Up
| Lesson | Who can do / see it | Limit and remaining | The cost of narrowing or measuring |
|---|---|---|---|
| identity/01 | group grants 231 permissions, 157 needed | 3 of 4 departed cannot be closed | narrowing drops 142 legitimate permissions |
| identity/02 | a single wildcard opens 132x the scope | explicit deny wins on 88 requests | the condition blocks 130 legitimate reads |
| identity/03 | 131 of 840 requests are permitted | 555 requests exceed the account limit | removing the documents stalls 15 jobs |
| identity/04 | 17 of 30 pairs are on the trust list | 480 remain before the call limit | closing the role stalls 90 operations a day |
| identity/05 | 79 key copies, 49 unregistered | 3 of 30 instances cannot get an identity | host-scoped grants 54 excess permissions |
| identity/06 | 46 items open, 27 needed | safe threshold 275 days, observed 90 | a 1-day window stalls 8 paths |
| identity/07 | blast radius 24 → 8 → 3 | quota overrun 1,142, 580 remaining | 24 documents grow to 57 |
| security/01 | decrypting pairs drop from 45 to 20 | 43 days remain in the key period | 132 legitimate reads a day stop |
| security/02 | 4 of 16 legs are unknown | 29 days remain in the transition window | 34 clients cannot be upgraded |
| security/03 | a human read 34 secrets in 90 days | 17 secrets were never rotated | the zero-miss window is 601,584 min |
| security/04 | 273 of 480 events, 88 by default | 11 subjects can delete the record | a separate account stalls 6 legitimate jobs |
| security/05 | 32 of 46 firings are real | scan quota 240, 108 remaining | remediation mode breaks 8 configurations |
| security/06 | 14 of 25 accesses come from the open internet | 12 of 18 endpoints full, 6 idle | closing it breaks 12 legitimate accesses |
| security/07 | 9 of 11 malicious, 8 from a single source | aggregation 60 GB/day, 5 remaining | at threshold 3, caught drops to 7 |
| security/08 | 7 of 26 evidence items come from the provider | evidence store 12 GB, 4.28 remaining | 139 units a year, 160 person-hours |
| cost/01 | 50.8% of the bill is a platform decision | 14 splits exist, 90 needed | the request crossover is 693,370 items |
| cost/02 | valid coverage 75.0% | 10 tag keys, 3 remaining | the mandate stalls 511.9 units of work |
| cost/03 | 86.4% of the budget is gone by the first alert | 204 units remain | the alert is open 68 of 90 days |
| cost/04 | p95 occupancy 31.4% | 16 tier steps remain on the ladder | 88.0% of savings require a disruption |
| cost/05 | 6/8 limits shared by five teams | the fullest has 3 units remaining | 116 limit-days, 2,195 units |
| cost/06 | 48.3% of the bill is standard-interface portable | 3 items cannot move, 37 special capabilities | break-even does not pay off below 48.9% |
| Course | Measure axis | Unit of measure |
|---|---|---|
| K01 Cloud Fundamentals | the cost of renting | delegated decision |
| K02 Compute and Networking in the Cloud | the delegated decision in a concrete item | irreversible item |
| K03 Storage and Data in the Cloud | when data is rented | egress and transfer |
| K04 Serverless and Edge Computing | the delegated decision comes back as a constraint | constraint item |
| K05 Cloud Governance, Security and Cost | the rented thing’s owner and limit | authority, visibility, quota |
The one question asked throughout M23 was what you delegate and what you give up in return. Five courses measured the same rented network five times, and every measurement took the same shape: a decision got delegated, an item became irreversible. In this course, that item gained an owner and a limit.
The exit column carried through all five courses, and its total is read here: three non-portable items, 257 person-days of work, an 879-unit exit bill, a 48.9% break-even. Lock-in is not a flaw in renting, it is its price.
To keep your progress and take notes, Log in
My notes
Log in to take notes.