Lesson 04 / 10
Capability Mapping
Building a traceability chain that runs from a business capability to the system that covers it and the data that system writes: the number of systems and owners touched when a capability changes, the difference between the short chain and the full chain, and the points where the chain breaks — untraceable capability, orphan system, ownerless data, unread data.
Contents
The previous lesson opened up a single process for a single capability and counted that the process passes through eight actors and five owners. The enterprise has thirteen capabilities, and a similar chain stands behind each one. When a capability changes — the loan-period rule is renewed, a membership condition changes — and someone checks whether anyone asked who should be notified, there is no list to look at: the inventory says which system covers which capability, but it does not say who reads the data that system writes. This lesson adds the missing link.
The Traceability Chain
Traceability is being able to travel without a break from a business capability to the pieces of software that carry it out, and to the data those pieces produce. The chain is built from three links (EA12): the capability, the systems that claim to cover it, and the data assets those systems write. One more link is added: the systems that read the written data. This last link is kept to a single hop; it does not extend to the data those readers themselves write.
What the chain measures is this: the set of systems touched when a capability’s definition changes. The direct link is the systems that cover that capability; they are the ones who carry out the change. The indirect link is the readers of the changed data; they may not change code, but they need to see the change, because the field they read is transformed. A coordination round is taken as one less than the number of distinct affected owners (EA13): an owner does not negotiate with itself, and every additional owner adds one round of reconciliation.
// enterprise/model.mjs — MODEL regional library network: systems, owners, capabilities, data. // Fictional; no real institution, vendor, product, or person is described. export const SYSTEM = { catalog: { owner: "external-provider", budget: "service-fee" }, loan: { owner: "it-department", budget: "internal-development" }, billing: { owner: "it-department", budget: "internal-development" }, membership: { owner: "member-services", budget: "member-services" }, identity: { owner: "municipal-it", budget: "municipality" }, "branch-local": { owner: "branch-management", budget: "branch" }, kiosk: { owner: "branch-management", budget: "branch" }, reporting: { owner: "management-unit", budget: "management" }, archive: { owner: "none", budget: "none" }, }; // CAPABILITY — the business capabilities the enterprise must cover (a list independent of system) export const CAPABILITY = ["material-search", "loan-issuance", "return-intake", "reservation", "membership-enrollment", "member-verification", "fee-calculation", "fee-collection", "inter-branch-transfer", "asset-count", "usage-reporting", "purchase-suggestion", "overdue-notification"]; // COVERS[s] = the capabilities system s claims to cover export const COVERS = { catalog: ["material-search", "asset-count"], loan: ["loan-issuance", "return-intake", "reservation", "overdue-notification"], billing: ["fee-calculation", "overdue-notification"], membership: ["membership-enrollment", "member-verification"], identity: ["member-verification"], "branch-local": ["return-intake", "inter-branch-transfer", "asset-count"], kiosk: ["loan-issuance", "material-search"], reporting: ["usage-reporting"], archive: [], }; // DATA[v] = data asset; which system writes it, which ones read it export const DATA = { "member-record": { writes: ["membership"], reads: ["loan", "billing", "kiosk", "reporting"] }, "identity-match": { writes: ["identity", "membership"], reads: ["loan", "kiosk"] }, "material-record": { writes: ["catalog"], reads: ["loan", "kiosk", "branch-local", "reporting"] }, "copy-status": { writes: ["catalog", "loan", "branch-local"], reads: ["kiosk", "reporting"] }, "loan-transaction": { writes: ["loan", "kiosk"], reads: ["billing", "reporting"] }, "fee-record": { writes: ["billing"], reads: ["membership", "reporting", "kiosk"] }, "penalty-rule": { writes: ["billing"], reads: ["loan", "kiosk"] }, "transfer-request": { writes: ["branch-local"], reads: ["loan", "catalog"] }, "legacy-record": { writes: [], reads: ["archive", "reporting"] }, "count-discrepancy": { writes: ["branch-local"], reads: [] }, }; export const SYSTEMS = Object.keys(SYSTEM); export const owner = (s) => SYSTEM[s].owner; // edge = the directed pair from the system that writes a data asset to the system that reads it export function edges() { const e = new Map(); for (const [v, d] of Object.entries(DATA)) for (const w of d.writes) for (const r of d.reads) if (w !== r) e.set(`${w}->${r}`, [...(e.get(`${w}->${r}`) ?? []), v]); return e; }
Measurement
The second file builds the chain for every capability, compares the two chain formats, and counts the break points.
// enterprise/tracing.mjs — capability -> system -> data -> reader chain; systems touched by a change import { SYSTEM, CAPABILITY, COVERS, DATA, SYSTEMS, owner } from "./model.mjs"; const col = (s, n) => String(s).padEnd(n); const covering = (y) => SYSTEMS.filter((s) => COVERS[s].includes(y)); const writes = (s) => Object.entries(DATA).filter(([, d]) => d.writes.includes(s)).map(([v]) => v); const ownerSet = (l) => new Set(l.map(owner).filter((o) => o !== "none")); function chain(y) { const direct = covering(y); const data = [...new Set(direct.flatMap(writes))]; const indirect = [...new Set(data.flatMap((v) => DATA[v].reads))].filter((s) => !direct.includes(s)); return { direct, data, indirect, all: [...direct, ...indirect] }; } console.log(col("business capability", 24) + col("system", 8) + col("data", 6) + col("readers", 8) + col("touched", 11) + col("owner", 7) + "coordination rounds"); console.log("-".repeat(88)); let shortSystems = 0, fullSystems = 0, shortRounds = 0, fullRounds = 0; const untraceable = []; for (const y of CAPABILITY) { const z = chain(y); if (z.direct.length === 0) { untraceable.push(y); continue; } const fullOwners = ownerSet(z.all).size, shortOwners = ownerSet(z.direct).size; shortSystems += z.direct.length; fullSystems += z.all.length; shortRounds += Math.max(0, shortOwners - 1); fullRounds += Math.max(0, fullOwners - 1); console.log(col(y, 24) + col(z.direct.length, 8) + col(z.data.length, 6) + col(z.indirect.length, 8) + col(z.all.length, 11) + col(fullOwners, 7) + (fullOwners - 1)); } const traced = CAPABILITY.length - untraceable.length; console.log(`\n${traced}/${CAPABILITY.length} capabilities are traceable; untraceable: ${untraceable.join(", ")}`); console.log(col("chain format", 28) + col("systems touched", 18) + col("per capability", 16) + "coordination rounds"); console.log("-".repeat(80)); console.log(col("short (capability->system)", 28) + col(shortSystems, 18) + col((shortSystems / traced).toFixed(2), 16) + shortRounds); console.log(col("full (->data->readers)", 28) + col(fullSystems, 18) + col((fullSystems / traced).toFixed(2), 16) + fullRounds); console.log(`diff: ${fullSystems - shortSystems} systems and ${fullRounds - shortRounds} coordination rounds ` + `do not show up in the short chain (${((fullSystems - shortSystems) / fullSystems * 100).toFixed(0)}%)`); const widest = CAPABILITY.filter((y) => covering(y).length) .map((y) => [y, chain(y).all.length]).sort((a, b) => b[1] - a[1])[0]; console.log(`widest chain: ${widest[0]} -> ${widest[1]} systems`); // ---- where the chain breaks ---- const brk = [ ["untraceable capability", untraceable], ["orphan system (cannot connect to a capability)", SYSTEMS.filter((s) => COVERS[s].length === 0)], ["ownerless system", SYSTEMS.filter((s) => SYSTEM[s].owner === "none")], ["ownerless data (no writer)", Object.keys(DATA).filter((v) => DATA[v].writes.length === 0)], ["unread data", Object.keys(DATA).filter((v) => DATA[v].reads.length === 0)], ]; console.log(""); console.log(col("break type", 50) + col("count", 6) + "items"); console.log("-".repeat(78)); for (const [t, l] of brk) console.log(col(t, 50) + col(l.length, 6) + (l.join(", ") || "-")); const total = brk.reduce((t, [, l]) => t + l.length, 0); const reached = new Set(CAPABILITY.filter((y) => covering(y).length).flatMap((y) => chain(y).data)); console.log(`\n${total} break points in total; ` + `${reached.size} of the ${Object.keys(DATA).length} data assets are reachable from some capability`);
business capability system data readers touched owner coordination rounds ---------------------------------------------------------------------------------------- material-search 2 3 4 6 4 3 loan-issuance 2 2 2 4 3 2 return-intake 2 4 4 6 4 3 reservation 1 2 3 4 3 2 membership-enrollment 1 2 4 5 4 3 member-verification 2 2 4 6 5 4 fee-calculation 1 2 4 5 4 3 inter-branch-transfer 1 3 4 5 4 3 asset-count 2 4 3 5 4 3 usage-reporting 1 0 0 1 1 0 overdue-notification 2 4 3 5 4 3 11/13 capabilities are traceable; untraceable: fee-collection, purchase-suggestion chain format systems touched per capability coordination rounds -------------------------------------------------------------------------------- short (capability->system) 17 1.55 5 full (->data->readers) 52 4.73 29 diff: 35 systems and 24 coordination rounds do not show up in the short chain (67%) widest chain: material-search -> 6 systems break type count items ------------------------------------------------------------------------------ untraceable capability 2 fee-collection, purchase-suggestion orphan system (cannot connect to a capability) 1 archive ownerless system 1 archive ownerless data (no writer) 1 legacy-record unread data 1 count-discrepancy 6 break points in total; 9 of the 10 data assets are reachable from some capability
The Number of Systems a Change Touches
The short chain shows only the covering systems: 17 systems in total for the eleven traceable capabilities, 1.55 per capability. The full chain adds the data and its readers too: 52 systems, 4.73 per capability. The 35 systems in between are systems that someone looking only at the short chain would never think to notify — 67% of the systems that will be touched never show up in the first link.
The coordination-round difference is sharper: 5 in total in the short chain, 29 in the full chain. What produces the cost at enterprise scale is not the size of the code change but these 24 extra rounds; every round is a reconciliation with a separate budget’s owner.
Two rows of the table show the extremes. The member-verification capability is covered by two systems, but its chain spreads across six systems and five owners; this is the most expensive change in the enterprise, because even the two covering systems have different owners. At the other end is usage reporting: one system, zero data, zero readers, zero coordination rounds. Reporting writes no data, it only reads; that is why a change made to it spreads nowhere. What determines a system’s change cost is not its size, it is how many readers the data it writes has.
Where the Chain Breaks
The chain cannot be built everywhere; there are six break points, and each has a different consequence.
There are two untraceable capabilities: fee collection and purchase suggestion. Both happen, but neither can be connected to any system, so the list of systems to touch when they change comes out empty. This does not mean the change is free; it means the cost is paid outside any system, at the counter and on hand-kept lists.
Orphan system and ownerless system land on the same item, the archive: it cannot be connected to any capability, and its owner is not recorded. Such a system never appears in the traceability table at all; both the decision to shut it down and the decision to keep it are left undefended.
The ownerless data is the legacy record — it is read but has no writer; nine of the ten data assets are reachable from a capability, and this is the one that is not. As seen in the previous lesson, this gap came back as a human decision inside the process. Unread data, on the other hand, is the count discrepancy: it is written, no one reads it. These are breaks at the two ends of the chain, and both say the same thing — a data asset’s value lies in both of its ends being connected.
Summary
- The traceability chain has four links: capability, covering systems, the data those systems write, and the systems that read that data; a coordination round is one less than the number of distinct affected owners.
- 11 of the 13 capabilities in the model enterprise are traceable; the remaining two cannot be connected to any system.
- The short chain shows 1.55 systems per capability, the full chain 4.73; 67% of the systems that will be touched do not show up in the first link, and the coordination rounds rise from 5 to 29.
- The widest chain is member verification: 6 systems, 5 owners, 4 coordination rounds. The narrowest chain is usage reporting: 1 system, 0 readers, 0 rounds — because it writes no data.
- The chain breaks at 6 points: 2 untraceable capabilities, 1 orphan system, 1 ownerless system, 1 ownerless data asset, 1 unread data asset; 9 of the 10 data assets are reachable from a capability.
Next Step
This topic modeled the enterprise in four steps: systems were mapped to capabilities, the same model was documented in two framework formats, a business process was classified step by step, and traceability from capability to data was built. What is now visible is which system a capability sits in and how many systems a change concerns. One thing remains invisible, and it is the subject of the second half of this course: how systems connect to each other. Behind every link in the chain that reads “this system reads that data” there is a transport form — which route the data travels, how often it is refreshed, who is responsible if it breaks. It was not for nothing that two questions in the frameworks lesson could not be answered in any format: the model never carried those fields. The next lesson turns the edge itself into an object of choice, and builds the same set of edges with different transport forms to count its cost.
To keep your progress and take notes, Log in
My notes
Log in to take notes.