---
title: 'Vendor Lock-In'
source: 'https://academia.sh/en/courses/cloud-governance/vendor-lock-in'
course: 'Cloud Governance, Security and Cost'
language: en
updated: '2026-09-13T05:07:34+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - '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'
---

# 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.

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**.

```js
// 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);
```

```js
// 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**.
