Skip to content
academia.sh

Lesson 03 / 12

Golden Paths

Measuring the recommended and supported path as a scope: a golden path is defined along the axes of language, data store, and deployment format, applied to five teams' requirements, the support a team loses by stepping outside is counted item by item, and as the scope expands, the number of teams that fit, the maintenance load, and the combination count are scanned together.

Published Updated

In this lesson

  • Defines a golden path as a scope along three axes and applies it to five teams' requirements
  • Counts, item by item and in person-days, the support a team loses by stepping outside the path
  • Expands the scope step by step and compares the number of fitting teams against maintenance load and combination count in the same table
Contents

The previous lesson compared two operating models and showed that neither one says the same thing: the platform’s boundary. The on-demand regime tracked order, the product regime tracked the breadth of the need; neither gave a written answer to the question “is this request in scope.”

The golden path is that written answer: the scope of the path the platform recommends and supports. A path is not a statement of good intentions, it is a scope definition — which language, which data store, which deployment format. The moment the scope is written, it splits everything in two: what stays inside gets support, what stays outside does not. This lesson writes the path as a scope, applies it to five teams, counts item by item what those left outside lose, and then expands the path. The through-line is the regional measurement network and its five teams; both the network and the teams are fictional.

Writing the Path as a Scope

PA13 — a golden path is defined as a scope along three axes: language, data store, deployment format. The path has ten support items, and every item is tied to a single axis. Every item carries two numbers: the person-days a team gains yearly from that item, and the maintenance the platform pays yearly for each option added to that axis. The reason: support is not a single whole; a team can get part of the path and not the rest, and that can only be measured once items are tied to axes.

PA14 — the five teams’ requirements are written along three axes; a team fits the path fully only if it is inside the scope on all three axes. PA15 — a team stepping outside an axis loses every support item tied to it, and keeps using the items of the axes it stays inside on. The reason: stepping outside the path is not all-or-nothing; partial benefit is also a condition that must be measured.

// path.mjs — a golden path defined as a scope and applied to five teams (model)
import { writeFileSync } from "node:fs";

// PA13 — a golden path is defined as a scope along three axes: language, data store, deployment format.
// The path has ten support items; each item is tied to a single axis. Value is the person-days a team
// gains yearly from that item; maintenance is what the platform pays yearly for each option added to that axis.
const ITEM = [
  ["scaffolding", "language", 6, 4], ["build pipeline", "language", 8, 5],
  ["dependency scanning", "language", 5, 3], ["test run", "language", 4, 3],
  ["schema migration", "store", 7, 5], ["backup and restore", "store", 9, 6],
  ["connection pool", "store", 4, 3], ["deployment step", "deployment", 10, 6],
  ["rollback step", "deployment", 6, 4], ["dashboard wiring", "deployment", 5, 3],
].map(([name, axis, value, maintenance]) => ({ name, axis, value, maintenance }));
const AXIS = ["language", "store", "deployment"];

// PA14 — the five teams' requirements are written along three axes; a team fits the path
// only if it is inside the scope on all three axes. Teams and requirements are fictional.
const TEAM = [
  ["meter reading", "language A", "time-series", "edge unit"],
  ["validation", "language A", "relational", "batch job"],
  ["billing", "language B", "relational", "long-lived service"],
  ["field", "language C", "document", "edge unit"],
  ["reporting", "language A", "relational", "long-lived service"],
].map(([name, language, store, deployment]) => ({ name, want: { language, store, deployment } }));

// PA15 — a team that steps outside loses every support item tied to the axis it is outside
// on, and keeps using the items of the axes it stays inside on.
const PATH0 = { language: ["language A"], store: ["relational"], deployment: ["long-lived service"] };
const outside = (path, e) => AXIS.filter((x) => !path[x].includes(e.want[x]));
const lost = (path, e) => ITEM.filter((k) => outside(path, e).includes(k.axis));
const gain = (path, e) => ITEM.filter((k) => !outside(path, e).includes(k.axis))
  .reduce((t, k) => t + k.value, 0);
writeFileSync("path.json", JSON.stringify({ ITEM, AXIS, TEAM, PATH0 }));

const axisTotal = (x) => ITEM.filter((k) => k.axis === x).reduce((t, k) => t + k.value, 0);
console.log("axis".padEnd(12) + "items".padStart(6) + "total value".padStart(14) +
  "maintenance/option".padStart(22) + "   items");
for (const x of AXIS) {
  const items = ITEM.filter((k) => k.axis === x);
  console.log(x.padEnd(12) + `${items.length}`.padStart(6) + `${axisTotal(x)}`.padStart(14) +
    `${items.reduce((t, k) => t + k.maintenance, 0)}`.padStart(22) + "   " + items.map((k) => k.name).join(", "));
}
console.log(`yearly gain for a team that fits the path fully: ` +
  `${ITEM.reduce((t, k) => t + k.value, 0)} person-days`);

const FULL = ITEM.reduce((t, k) => t + k.value, 0);
console.log("\npath: language A / relational / long-lived service");
console.log("team".padEnd(15) + "axes fit".padStart(12) + "gain".padStart(8) +
  "path share".padStart(12) + "lost items".padStart(13) + "lost value".padStart(13) +
  "   axes left outside");
for (const e of TEAM) {
  const d = outside(PATH0, e), L = lost(PATH0, e);
  console.log(e.name.padEnd(15) + `${AXIS.length - d.length}/3`.padStart(12) +
    `${gain(PATH0, e)}`.padStart(8) +
    `${(100 * gain(PATH0, e) / FULL).toFixed(0)}%`.padStart(12) + `${L.length}`.padStart(13) +
    `${L.reduce((t, k) => t + k.value, 0)}`.padStart(13) +
    "   " + (d.length ? d.map((x) => `${x} (${e.want[x]})`).join(", ") : "-"));
}
console.log("teams left outside by axis — " +
  AXIS.map((x) => `${x}: ${TEAM.filter((e) => outside(PATH0, e).includes(x)).length}`).join(", "));

console.log("\nsupport lost item by item for those who step outside:");
for (const e of TEAM.filter((e) => outside(PATH0, e).length))
  console.log(`  ${e.name}: ` + lost(PATH0, e).map((k) => `${k.name} (${k.value})`).join(", "));
axis         items   total value    maintenance/option   items
language         4            23                    15   scaffolding, build pipeline, dependency scanning, test run
store            3            20                    14   schema migration, backup and restore, connection pool
deployment       3            21                    13   deployment step, rollback step, dashboard wiring
yearly gain for a team that fits the path fully: 64 person-days

path: language A / relational / long-lived service
team               axes fit    gain  path share   lost items   lost value   axes left outside
meter reading           1/3      23         36%            6           41   store (time-series), deployment (edge unit)
validation              2/3      43         67%            3           21   deployment (batch job)
billing                 2/3      41         64%            4           23   language (language B)
field                   0/3       0          0%           10           64   language (language C), store (document), deployment (edge unit)
reporting               3/3      64        100%            0            0   -
teams left outside by axis — language: 2, store: 2, deployment: 3

support lost item by item for those who step outside:
  meter reading: schema migration (7), backup and restore (9), connection pool (4), deployment step (10), rollback step (6), dashboard wiring (5)
  validation: deployment step (10), rollback step (6), dashboard wiring (5)
  billing: scaffolding (6), build pipeline (8), dependency scanning (5), test run (4)
  field: scaffolding (6), build pipeline (8), dependency scanning (5), test run (4), schema migration (7), backup and restore (9), connection pool (4), deployment step (10), rollback step (6), dashboard wiring (5)

Who Is Left Outside

A team that fits the path fully gains 64 person-days a year. On the baseline path — language A, relational store, long-lived service — only one of the five teams collects that gain: reporting. The other four are outside on seven team-axis pairs combined.

Being outside has degrees, and the table separates them. validation is inside on two of the three axes: it gets language and store support and loses only the deployment items — deployment step (10), rollback step (6), dashboard wiring (5), 21 person-days in total. billing is also 2/3, but the axis it loses is language: four items, 23 person-days. meter reading is outside on two axes and loses six items, 41 person-days — since language is the only axis it stays inside on, its gain stays at 23.

field is inside on zero axes. It loses all ten support items and gets nothing from the path. What this distinction is worth deciding: for validation, stepping outside the path is a 21-person-day shortfall; for field, the path existing and the path not existing are the same thing. The same path, in the same organization, looks like a small shortfall to one team and total absence to another.

Partially fitting teams are what this course calls a leaky abstraction. meter reading is inside the platform — it gets scaffolding, the build pipeline, dependency scanning, and the test run from the platform — but builds its own schema migration, backup, connection pool, deployment, rollback, and dashboard wiring. The team has to know both worlds at once; the simplification the path gives it stays at 36%. It is 67% for validation, 64% for billing. The path’s average effect across these five teams is 53%, though the description “every team but one gets partial support” makes it sound far better than it is.

The last line says which axis to expand: two teams are outside on the language axis, two on the store axis, and three on the deployment axis. The scope decision starts with the axis that strikes the most teams — in the table, that axis is the deployment format.

Expanding the Path

PA16 — a path’s maintenance load is each support item’s maintenance multiplied by the number of options on the axis it is tied to; every option added to scope brings the maintenance of that axis’s entire item set back once more. PA17 — a path’s own complexity is the number of combinations that must be tested (language × store × deployment), and every combination costs 3 person-days a year. The reason: as scope expands, items do not just multiply, they collide with each other; a language working with one store does not mean it will work with another. PA18 — expansion adds one option to one axis at each step.

// expand.mjs — the path's scope is expanded step by step
import { readFileSync } from "node:fs";
const { ITEM, AXIS, TEAM, PATH0 } = JSON.parse(readFileSync("path.json", "utf8"));

// PA16 — the path's maintenance load is each support item's maintenance multiplied by the number
// of options on the axis it is tied to: every option added to scope brings that axis's full set
// of items' maintenance back once more. PA17 — the path's own complexity is the number of
// combinations that must be tested (language x store x deployment), and every combination costs
// 3 person-days a year. PA18 — expansion adds one option to one axis at each step.
const COMBINATION_COST = 3;
const STEP = [
  ["deployment", "batch job"], ["language", "language B"], ["store", "time-series"],
  ["deployment", "edge unit"], ["language", "language C"], ["store", "document"],
];
const outside = (path, e) => AXIS.filter((x) => !path[x].includes(e.want[x]));
const measure = (path) => {
  const gain = TEAM.reduce((t, e) => t + ITEM
    .filter((k) => !outside(path, e).includes(k.axis)).reduce((a, k) => a + k.value, 0), 0);
  const maintenance = ITEM.reduce((t, k) => t + k.maintenance * path[k.axis].length, 0);
  const combinations = AXIS.reduce((t, x) => t * path[x].length, 1);
  return { gain, maintenance, combinations, load: maintenance + combinations * COMBINATION_COST,
    fits: TEAM.filter((e) => outside(path, e).length === 0).length,
    stepOuts: TEAM.reduce((t, e) => t + outside(path, e).length, 0) };
};

const path = { language: [...PATH0.language], store: [...PATH0.store], deployment: [...PATH0.deployment] };
const STAGE = [{ name: "baseline", ...measure(path), scope: "1/1/1" }];
for (const [axis, option] of STEP) {
  path[axis] = [...path[axis], option];
  STAGE.push({ name: `+${option}`, ...measure(path),
    scope: AXIS.map((x) => path[x].length).join("/") });
}
console.log("stage".padEnd(16) + "scope".padStart(8) + "fitting teams".padStart(14) +
  "step-outs".padStart(13) + "gain".padStart(8) + "maintenance".padStart(13) +
  "combos".padStart(9) + "combo load".padStart(14) + "total load".padStart(12) +
  "net".padStart(6));
for (const s of STAGE)
  console.log(s.name.padEnd(16) + s.scope.padStart(8) + `${s.fits}/5`.padStart(14) +
    `${s.stepOuts}`.padStart(13) + `${s.gain}`.padStart(8) + `${s.maintenance}`.padStart(13) +
    `${s.combinations}`.padStart(9) + `${s.combinations * COMBINATION_COST}`.padStart(14) +
    `${s.load}`.padStart(12) + `${s.gain - s.load}`.padStart(6));

const best = STAGE.reduce((a, b) => (a.gain - a.load >= b.gain - b.load ? a : b));
const last = STAGE[STAGE.length - 1], first = STAGE[0];
console.log(`\nhighest net: "${best.name}" stage, scope ${best.scope}, ` +
  `${best.fits}/5 teams fit, net ${best.gain - best.load} person-days`);
console.log(`stage where all five teams fit (${last.scope}): net ${last.gain - last.load} — ` +
  `${first.gain - first.load - last.gain + last.load} person-days below the baseline ` +
  `(${first.scope}, ${first.fits}/5 teams)`);
console.log(`going from a one-option scope to a three-option scope, gain rose ` +
  `${(100 * (last.gain / first.gain - 1)).toFixed(0)}%, load rose ` +
  `${(100 * (last.load / first.load - 1)).toFixed(0)}%`);
stage              scope fitting teams    step-outs    gain  maintenance   combos    combo load  total load   net
baseline           1/1/1           1/5            7     171           42        1             3          45   126
+batch job         1/1/2           2/5            6     192           55        2             6          61   131
+language B        2/1/2           3/5            5     215           70        4            12          82   133
+time-series       2/2/2           3/5            4     235           84        8            24         108   127
+edge unit         2/2/3           4/5            2     277           97       12            36         133   144
+language C        3/2/3           4/5            1     300          112       18            54         166   134
+document          3/3/3           5/5            0     320          126       27            81         207   113

highest net: "+edge unit" stage, scope 2/2/3, 4/5 teams fit, net 144 person-days
stage where all five teams fit (3/3/3): net 113 — 13 person-days below the baseline (1/1/1, 1/5 teams)
going from a one-option scope to a three-option scope, gain rose 87%, load rose 360%

The scan starts in the expected direction. Once batch job is added to the deployment axis, validation fits the path and net rises from 126 to 131; once language B is added, billing fits and net reaches 133. The break appears at the third step: the time-series store is added to scope, gain rises from 215 to 235, but the number of fitting teams does not change — meter reading is still waiting on the edge unit — and net drops from 133 to 127. Scope has grown, and no one has entered the path.

The fourth step is the high point: once the edge unit is added, meter reading fits the path and field gains the deployment items, and net reaches 144 person-days. The scope is 2/2/3, and four of the five teams fit. Everything past this loses ground: adding language C brings net down to 134, and adding the document store brings it down to 113.

The last line is the lesson’s verdict. The path that fits all five teams (3/3/3) is 13 person-days worse than the path that was never expanded at all — even though only one of the five teams fit the baseline path. As scope goes from a one-option axis to a three-option axis, gain rose 87%, load rose 360%. The number of combinations that must be tested went from one to twenty-seven; the path’s own complexity overtook the complexity it was meant to remove. A path that covers everyone is no longer a path.

Summary

  • The golden path was written as a scope along three axes (language, data store, deployment format) and ten support items were tied to those axes; a team that fits the path fully gains 64 person-days a year.
  • On the baseline path, only reporting fits fully among the five teams; the other four are outside on seven team-axis pairs combined.
  • The degree of being outside was measured: validation loses 21 person-days, billing 23, meter reading 41; field loses all ten items and gets nothing from the path.
  • A partially fitting team is where the abstraction leaks: meter reading gets only 36% of the path’s simplification, builds the rest itself, and has to know both worlds at once.
  • As scope expands, net follows 126, 131, 133, 127, 144, 134, and 113 person-days; the high point is at a 2/2/3 scope, the stage where four of the five teams fit.
  • The path that fits all five teams is 13 person-days worse than the baseline path; as scope grew to three options, gain rose 87% and load rose 360%, and the number of combinations to test went from one to twenty-seven.

Next Step

In this lesson the path was written as a scope, and both sides of that scope were counted: the gain for those who stay inside, the loss for those left outside. Something the path’s definition never said was this — was entering the path mandatory? Throughout the table, teams’ requirements were held fixed, and the path fit them only as much as it fit. If mandate had been imposed, would field have given up the edge unit, or would the path have blocked a legitimate requirement of theirs? The next lesson tries three modes for every item — mandatory, default but changeable, free — and counts diversity and alignment together: how many distinct choices emerge, in how many items the shared tool becomes useless, and in how many teams mandate blocks a genuine requirement.

To keep your progress and take notes, Log in

My notes

Log in to take notes.

Start typing to search.

↑↓ ↵ Esc navigate · open · close