Skip to content
academia.sh

Lesson 01 / 12

What Is Platform Engineering

Measuring the platform as an abstraction: the eighteen items that seven courses leave for every team to rebuild are listed by type, the rebuild work per team is counted, scanning the team count derives the threshold where the platform pays for itself, and the person-days the platform costs below that threshold appear in the same table.

Published Updated

In this lesson

  • Counts the item set left by seven courses type by type and measures the per-team rebuild work in person-days
  • Scans the team count to derive the threshold where the platform pays for itself and the work it costs below that threshold
  • Counts the items that do not fit the platform's shape and shows how exemptions push the threshold upward
Contents

The previous course closed out hardening and left a question behind. Across seven courses, each layer was built separately: an environment was provisioned, a pipeline was set up, a cluster object was written, an infrastructure record was kept, a dashboard was wired, the default surface was narrowed. Is every team going to rebuild all of this?

The answer is not an opinion, it is a number: the cost of moving something to a shared place is paid up front, its gain is multiplied by the team count, and the two become equal at a specific team count. This course’s unit of measure is abstraction — a platform hides a layer and puts an interface in its place, and that interface carries its own cost. Every lesson counts three things: what got hidden and how much each team saved, where the hidden part leaked back through, and who had to step outside the path.

Where a platform stands as a team type — stream-aligned teams, cognitive load, team topologies — was measured in the Team Topologies lesson; it is not repeated here. Here the platform is not a team arrangement, it is an abstraction. The through-line is the regional measurement network; this course adds the five teams that build the network’s software: meter reading, validation, billing, field, and reporting. Both the network and the teams are fictional.

The Item Set Left by Seven Courses

PA1 — the work left behind by seven courses splits into eighteen items across six types: environment, pipeline, cluster object, record, dashboard, and hardening step. Every item carries three numbers: the cost of a team building the item itself and running it for three years, the cost of the platform taking the item over once, and the work remaining with the team after the handoff. The unit is the person-day. The reason: the platform argument cannot be measured until the item set is counted.

PA2 — an item’s threshold is the handoff cost divided by the net per-team gain, rounded up; the net gain is the difference between the per-team work and the work remaining with the team. The reason: the handoff is paid once, the gain repeats with every team.

PA3 — two regimes are compared: one where every team builds all eighteen items itself, and one where the platform takes all of them over. PA4 — a third regime, selective handoff, moves only the items whose threshold is five or below; the rest stay with teams at full cost. The reason: handoff is not an organizational decision, it is made item by item.

// item.mjs — the item set left by seven courses, per-item threshold and team scan (model)
import { writeFileSync } from "node:fs";

// PA1 — the 18 items left by seven courses and rebuilt by every team: type, name, per-team
// work, platform handoff (once), work left with the team after handoff. Unit is person-day,
// window three years. PA2 — item threshold = ceil(handoff / (team - remaining)). Fictional.
const ITEM = [
  ["environment", "development environment", 12, 55, 3],
  ["environment", "test environment", 9, 40, 2],
  ["environment", "secret binding", 7, 46, 2],
  ["pipeline", "build step", 8, 30, 2],
  ["pipeline", "test run", 6, 22, 4],
  ["pipeline", "evidence generation", 10, 44, 1],
  ["pipeline", "deployment step", 11, 52, 3],
  ["cluster object", "workload definition", 9, 38, 3],
  ["cluster object", "network rule", 7, 34, 2],
  ["cluster object", "scaling rule", 5, 18, 3],
  ["record", "infrastructure state record", 13, 62, 2],
  ["record", "version record", 6, 20, 1],
  ["dashboard", "four-dashboard wiring", 8, 30, 5],
  ["dashboard", "log aggregation", 7, 26, 2],
  ["dashboard", "alert rule", 9, 24, 6],
  ["hardening", "base image scanning", 10, 42, 1],
  ["hardening", "privilege narrowing", 12, 50, 4],
  ["hardening", "secret rotation", 8, 33, 2],
].map(([type, name, team, handoff, remaining]) => ({ type, name, team, handoff, remaining,
  net: team - remaining, threshold: Math.ceil(handoff / (team - remaining)), leak: remaining / team }));
writeFileSync("item.json", JSON.stringify(ITEM));

const T = (d, f) => d.reduce((t, k) => t + f(k), 0);
const share = (a, b) => `${(100 * a / b).toFixed(1)}%`;
const group = (t) => ITEM.filter((k) => k.type === t);
console.log(`${ITEM.length} items — team side ${T(ITEM, (k) => k.team)}, handoff ` +
  `${T(ITEM, (k) => k.handoff)}, remaining with the team after handoff ${T(ITEM, (k) => k.remaining)} ` +
  `person-days (remaining share ${share(T(ITEM, (k) => k.remaining), T(ITEM, (k) => k.team))})`);
console.log("remaining share by type — " + [...new Set(ITEM.map((k) => k.type))]
  .map((t) => `${t} ${share(T(group(t), (k) => k.remaining), T(group(t), (k) => k.team))}`).join(", "));

console.log("\n" + "item".padEnd(29) + "team".padStart(6) + "remaining".padStart(11) +
  "handoff".padStart(9) + "threshold".padStart(11));
for (const k of [...ITEM].sort((a, b) => a.threshold - b.threshold || a.name.localeCompare(b.name)))
  console.log(k.name.padEnd(29) + `${k.team}`.padStart(6) + `${k.remaining}`.padStart(11) +
    `${k.handoff}`.padStart(9) + `${k.threshold}`.padStart(11));

// PA3 — two regimes: every team builds all 18 items itself; or the platform takes them all over and a remainder stays with the team.
// PA4 — a third regime, selective handoff, moves only the items whose threshold is five or below; the rest stay with teams at full cost.
const FULL = T(ITEM, (k) => k.team);
const ALL_HANDOFF = T(ITEM, (k) => k.handoff), ALL_REMAINING = T(ITEM, (k) => k.remaining);
const SEL_HANDOFF = T(ITEM.filter((k) => k.threshold <= 5), (k) => k.handoff);
const SEL_REMAINING = T(ITEM, (k) => (k.threshold <= 5 ? k.remaining : k.team));
const sign = (v) => (v > 0 ? `+${v}` : `${v}`);
console.log("\n" + "team".padStart(6) + "no platform".padStart(13) + "all items".padStart(14) +
  "diff".padStart(8) + "selective".padStart(11) + "diff".padStart(8) + "winner".padStart(14));
for (let n = 1; n <= 12; n++) {
  const none = n * FULL, all = ALL_HANDOFF + n * ALL_REMAINING, sel = SEL_HANDOFF + n * SEL_REMAINING;
  const best = Math.min(none, all, sel);
  console.log(`${n}`.padStart(6) + `${none}`.padStart(13) + `${all}`.padStart(14) +
    sign(none - all).padStart(8) + `${sel}`.padStart(11) + sign(none - sel).padStart(8) +
    (best === none ? "no platform" : best === sel ? "selective" : "all items").padStart(14));
}
const threshold = (handoff, kept) => Math.ceil(handoff / (FULL - kept));
console.log(`\nthreshold if all items are handed off: ${threshold(ALL_HANDOFF, ALL_REMAINING)} teams; ` +
  `selective handoff: ${threshold(SEL_HANDOFF, SEL_REMAINING)} teams (handoff ${ALL_HANDOFF} and ${SEL_HANDOFF})`);
const heavy = ITEM.filter((k) => k.leak > 0.4).sort((a, b) => b.threshold - a.threshold);
console.log(`items with zero remaining work: ${ITEM.filter((k) => k.remaining === 0).length} / ` +
  `${ITEM.length}; items whose remainder exceeds 40% of team work: ${heavy.length} — ` +
  heavy.map((k) => `${k.name} (${share(k.remaining, k.team)}, threshold ${k.threshold})`).join(", "));
18 items — team side 157, handoff 666, remaining with the team after handoff 48 person-days (remaining share 30.6%)
remaining share by type — environment 25.0%, pipeline 28.6%, cluster object 38.1%, record 15.8%, dashboard 54.2%, hardening 23.3%

item                           team  remaining  handoff  threshold
version record                    6          1       20          4
base image scanning              10          1       42          5
build step                        8          2       30          5
evidence generation              10          1       44          5
infrastructure state record      13          2       62          6
log aggregation                   7          2       26          6
secret rotation                   8          2       33          6
test environment                  9          2       40          6
deployment step                  11          3       52          7
development environment          12          3       55          7
network rule                      7          2       34          7
privilege narrowing              12          4       50          7
workload definition               9          3       38          7
alert rule                        9          6       24          8
scaling rule                      5          3       18          9
four-dashboard wiring             8          5       30         10
secret binding                    7          2       46         10
test run                          6          4       22         11

  team  no platform     all items    diff  selective    diff        winner
     1          157           714    -557        264    -107   no platform
     2          314           762    -448        392     -78   no platform
     3          471           810    -339        520     -49   no platform
     4          628           858    -230        648     -20   no platform
     5          785           906    -121        776      +9     selective
     6          942           954     -12        904     +38     selective
     7         1099          1002     +97       1032     +67     all items
     8         1256          1050    +206       1160     +96     all items
     9         1413          1098    +315       1288    +125     all items
    10         1570          1146    +424       1416    +154     all items
    11         1727          1194    +533       1544    +183     all items
    12         1884          1242    +642       1672    +212     all items

threshold if all items are handed off: 7 teams; selective handoff: 5 teams (handoff 666 and 136)
items with zero remaining work: 0 / 18; items whose remainder exceeds 40% of team work: 4 — test run (66.7%, threshold 11), four-dashboard wiring (62.5%, threshold 10), scaling rule (60.0%, threshold 9), alert rule (66.7%, threshold 8)

Where the Threshold Lies

The first two lines give the upfront cost: a single team building all seven courses’ worth of items itself and running them for three years costs 157 person-days; the platform taking over the same items costs 666 person-days — more than four times a single team’s work. The work still sitting with the team after handoff is 48 person-days, 30.6% of per-team work. The highest remainder by type is in the dashboard items (54.2%), because which metric to watch and at what value an alert fires depends on the team’s own work; the lowest is in the record items (15.8%), since how an infrastructure state record is kept does not change from team to team.

The table decides item by item. version record pays for itself at four teams; test run does not pay for itself before eleven teams, because four of its six person-days stay with the team and the net gain drops to two. In a five-team organization, only four of the eighteen items pay for themselves.

The scan shows three zones. Between one and four teams, the no-platform regime wins; at a single team, handing off all items costs 557 person-days. At five and six teams, selective handoff takes the lead. Past seven teams, handing off all items wins and the gap widens: 642 person-days at twelve teams. The five-team row is this course’s first decision: when five teams move all items to the platform, they lose 121 person-days, 15.4% above the no-platform regime; the same five teams gain 9 person-days with selective handoff. The question is not “should we build a platform,” it is “how many teams, and which items.”

The last line names the leak: none of the eighteen items has a zero remainder, four exceed 40% of the team’s work, and those four have thresholds of 8, 9, 10, and 11 — the slowest-paying end of the list. The two numbers share a source: as the remainder grows, the net gain shrinks and the threshold climbs. The item that leaks the most is the item that pays for itself last.

Five Teams and Who Steps Outside

PA5 — five teams build the measurement network’s software, and every team has items that do not fit the platform’s shape. PA6 — a team that steps outside pays the item’s full cost, plus a ceil(team / 4) alignment cost to reconcile its own shape with what the platform produces. The reason: stepping outside is not the same as escaping the platform; the team still touches the platform’s boundary and has to convert its output into the shape the platform expects.

// teams.mjs — five teams, the items they step outside of, and the threshold with exemptions
import { readFileSync } from "node:fs";
const ITEM = JSON.parse(readFileSync("item.json", "utf8"));
const find = (name) => ITEM.find((k) => k.name === name);

// PA5 — the five teams that build the regional measurement network's software, and the items
// that do not fit the platform's shape; teams and reasons are both fictional. PA6 — a team
// that steps outside pays the item's full cost, plus a ceil(team / 4) alignment cost to reconcile its own shape with what the platform produces.
const TEAM = [
  ["meter reading", { "development environment": "hardware emulation", "network rule": "direct line" }],
  ["validation", { "scaling rule": "nightly batch run", "workload definition": "long-running job" }],
  ["billing", { "infrastructure state record": "retention requirement",
    "version record": "retroactive accounting", "secret rotation": "a separate signing chain" }],
  ["field", { "deployment step": "offline operation", "log aggregation": "intermittent connectivity",
    "development environment": "hardware emulation" }],
  ["reporting", {}],
].map(([name, outside]) => ({ name, outside }));

const alignment = (k) => Math.ceil(k.team / 4);
const FULL = ITEM.reduce((t, k) => t + k.team, 0);
const cost = (e, eligible) => ITEM.reduce((t, k) => t + (eligible(k)
  ? (k.name in e.outside ? k.team + alignment(k) : k.remaining) : k.team), 0);
console.log("team".padEnd(15) + "stepped outside".padStart(16) + "work outside".padStart(14) +
  "remainder inside".padStart(18) + "total".padStart(8) + "saved".padStart(10));
for (const e of TEAM) {
  const d = Object.keys(e.outside);
  e.outsideWork = d.reduce((t, name) => t + find(name).team + alignment(find(name)), 0);
  e.insideWork = ITEM.filter((k) => !d.includes(k.name)).reduce((t, k) => t + k.remaining, 0);
  e.total = e.outsideWork + e.insideWork;
  console.log(e.name.padEnd(15) + `${d.length}`.padStart(16) + `${e.outsideWork}`.padStart(14) +
    `${e.insideWork}`.padStart(18) + `${e.total}`.padStart(8) + `${FULL - e.total}`.padStart(10));
}

const PAID = TEAM.reduce((t, e) => t + e.total, 0);
const HANDOFF = ITEM.reduce((t, k) => t + k.handoff, 0);
const touched = new Set(TEAM.flatMap((e) => Object.keys(e.outside)));
const avg = PAID / TEAM.length;
console.log(`\nitems at least one team steps outside of: ${touched.size} / ${ITEM.length}; ` +
  `total step-outs: ${TEAM.reduce((t, e) => t + Object.keys(e.outside).length, 0)}`);
console.log(`5 teams — no platform ${5 * FULL}, all items without exemptions ${HANDOFF + 5 * 48}, ` +
  `all items with exemptions ${HANDOFF + PAID} person-days`);
console.log(`average team cost with exemptions ${avg.toFixed(1)}; net gain ` +
  `${(FULL - avg).toFixed(1)}; threshold ${Math.ceil(HANDOFF / (FULL - avg))} teams`);

// Selective handoff is tried again on the same five teams, exemptions left in place.
const selective = (k) => k.threshold <= 5;
const SEL = ITEM.filter(selective).reduce((t, k) => t + k.handoff, 0) +
  TEAM.reduce((t, e) => t + cost(e, selective), 0);
console.log(`5 teams, selective handoff, with exemptions: ${SEL} person-days — vs. no platform ` +
  `${5 * FULL - SEL} person-days, ${(100 * (5 * FULL - SEL) / (5 * FULL)).toFixed(1)}%`);
team            stepped outside  work outside  remainder inside   total     saved
meter reading                 2            24                43      67        90
validation                    2            19                42      61        96
billing                       3            35                43      78        79
field                         3            38                40      78        79
reporting                     0             0                48      48       109

items at least one team steps outside of: 9 / 18; total step-outs: 10
5 teams — no platform 785, all items without exemptions 906, all items with exemptions 998 person-days
average team cost with exemptions 66.4; net gain 90.6; threshold 8 teams
5 teams, selective handoff, with exemptions: 783 person-days — vs. no platform 2 person-days, 0.3%

Only one team is the team the platform was described for: reporting takes over all eighteen items and saves 109 person-days. The other four give back part of what they save. billing is outside three items — because of the retention requirement, retroactive accounting, and the separate signing chain — and saves 79 person-days, thirty behind reporting. field also saves 79 person-days, but its outside work is heavier (38 against 35), because offline operation moves the entire deployment step outside.

In nine of the eighteen items, at least one team is outside the path; half the item set cannot serve all five teams at once. development environment is outside for two teams at once: both meter reading and field need hardware emulation.

The bill for exemptions is in the last two lines. In the exemption-free accounting, five teams paid 906 person-days; with exemptions it rises to 998 person-days, 27% above the no-platform regime (785). Because the average team cost rises from 48 to 66.4, the net gain falls from 109 to 90.6 and the threshold shifts from seven teams to eight. Selective handoff cannot escape it either: billing is outside one of the four selected items, version record; selective handoff’s gain at five teams falls from 9 person-days to 2 person-days, 0.3% of the 785-person-day total. A difference this small carries no decision.

Summary

  • The work left by seven courses split into eighteen items across six types: one team building everything itself and running it for three years costs 157 person-days, the platform taking it all over costs 666.
  • The remainder left with the team after handoff is 48 person-days, 30.6% of per-team work; no item has a zero remainder, and the highest is in the dashboard items (54.2%).
  • The team scan produced three zones: the no-platform regime wins at 1–4 teams, selective handoff at 5–6 teams, and handing off all items wins past 7 teams. Handing off everything at five teams costs 121 person-days.
  • In a five-team organization, only four of the eighteen items pay for themselves; the four leakiest items have thresholds of 8, 9, 10, and 11 — the threshold climbs as the remainder grows.
  • Four of the five teams step outside at least one item, and nine of the eighteen items carry an exemption. Exemptions push the five-team total from 906 to 998 person-days, the threshold from seven teams to eight, and selective handoff’s gain from 9 person-days to 2 (0.3%).

Next Step

Here the platform was built as a cost calculation: the item handed off, the remainder left with the team, and the threshold where it pays for itself. The calculation assumed the item costs were known; whether teams actually wanted a given item was never asked. Once the handoff cost is paid, an item going unused turns it into a dead part of the 666-person-day investment. The next lesson runs the platform in two operating models — on-demand and product-managed — and measures adoption, capabilities that go unused, the work spent on them, the length of the feedback loop, and the number of people the product approach pulls from the product teams.

To keep your progress and take notes, Log in

My notes

Log in to take notes.

Start typing to search.

↑↓ ↵ Esc navigate · open · close