---
title: 'Capability Mapping'
source: 'https://academia.sh/en/courses/enterprise-context/capability-mapping'
course: 'Enterprise Context and Integration'
language: en
updated: '2026-08-23T07:01:06+00:00'
license: 'CC BY-SA 4.0'
---

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

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.

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

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