Lesson 03 / 10
Business Process Modeling
Modeling a business process step by step and sorting every step into three classes — fully automatic, requiring human judgment, outside the system: deriving the automation boundary from the model, the number of actors and owners the process passes through, and the number of wrong decisions and rollbacks produced by automating a step that requires human judgment by forcing the boundary.
Contents
The previous two lessons treated the enterprise as a static structure: systems, owners, capabilities, records. But a business capability is a single row in the inventory, yet as it happens it is a process that unfolds step by step. Behind the “loan issuance” row are dozens of operations lined up from the card scan to the security tag, and not all of them happen inside software. This lesson opens up that row and ties down where automation stops with a rule.
A Process Is a Step Sequence
Process notations differ in their symbol set; no brand or notation name is written here. What they share is this: every step has an actor, a set of inputs, and an output. Once these three fields are written down, the notation itself becomes secondary, because the fields are what can be measured.
An actor is not always a system. The fee being collected at the counter, and the security tag being deactivated, are both steps of the process; both have an actor outside software. A process model’s first job is not to delete these steps but to classify them. Three classes are used: fully automatic, requiring human judgment, outside the system.
Classification is not left to opinion, it is derived from the model (EA9): a step is automatic only if every one of its inputs comes from a data asset that has a writer, and no information item the decision needs falls outside the data model. This rule separates two different obstacles from each other. The first is that the information exists nowhere — a material’s damage degree is in no data asset. The second is that the information appears to exist as a data asset, but no system writes it; the legacy record in the enterprise model is like this, and it is called ownerless data.
// 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 models the loan checkout process as sixteen steps, derives classification from the rule, and runs both boundaries over the same two hundred transactions. How often steps requiring human judgment occur, how many steps a wrong decision rolls back, and how many systems the fix requires writing to are chosen values (EA10). The probability of a wrong decision per missing information item was taken as 0.15; that is the source of the threshold, and its sensitivity is printed separately (EA11).
// enterprise/process.mjs — loan checkout process: step classes, automation boundary, cost of forcing it import { DATA, owner } from "./model.mjs"; const col = (s, n) => String(s).padEnd(n); // STEP — MODEL loan checkout process; system null means the step happens outside software (counter, shelf). // missing = the number of information items the step needs but that appear in no data asset. const STEP = [ { name: "member scans card", system: "kiosk", input: [] }, { name: "identity is verified", system: "identity", input: ["identity-match"] }, { name: "membership status is queried", system: "membership", input: ["member-record"] }, { name: "unpaid fee is checked", system: "billing", input: ["fee-record"] }, { name: "accrued fee is collected at the counter", system: null, input: [] }, { name: "material barcode is scanned", system: "kiosk", input: ["material-record"] }, { name: "copy status is queried", system: "catalog", input: ["copy-status"] }, { name: "transfer is opened for a copy at another branch", system: "branch-local", input: ["copy-status"], missing: 2, frequency: 0.12, rollbackSteps: 3, fix: ["branch-local", "catalog", "loan"] }, { name: "material's damage degree is recorded", system: "loan", input: ["material-record"], missing: 1, frequency: 0.06, rollbackSteps: 2, fix: ["loan", "billing"] }, { name: "loan period is calculated", system: "loan", input: ["penalty-rule", "member-record"] }, { name: "researcher extension is granted", system: "loan", input: ["member-record"], missing: 2, frequency: 0.04, rollbackSteps: 2, fix: ["loan", "membership"] }, { name: "debt in the legacy record is queried", system: "loan", input: ["legacy-record"], frequency: 0.09, rollbackSteps: 4, fix: ["loan", "billing", "reporting"] }, { name: "loan record is written", system: "loan", input: ["member-record"] }, { name: "copy status is updated", system: "loan", input: ["copy-status"] }, { name: "security tag is deactivated", system: null, input: [] }, { name: "overdue notification is scheduled", system: "loan", input: ["loan-transaction", "penalty-rule"] }, ]; // Automation boundary rule: a step can only be automated if every one of its inputs is a data // asset with a writer, and no information item it needs falls outside the data model. const unsourced = (a) => a.input.filter((v) => DATA[v].writes.length === 0); function classify(a) { if (a.system === null) return ["non-system", "happens outside any system"]; if (a.missing) return ["human-decision", `${a.missing} information item(s) in no data asset`]; if (unsourced(a).length) return ["human-decision", `data with no writer: ${unsourced(a)[0]}`]; return ["automatic", "-"]; } console.log(col("step", 50) + col("actor", 15) + col("class", 16) + "reason"); console.log("-".repeat(102)); for (const a of STEP) { const [s, n] = classify(a); console.log(col(a.name, 50) + col(a.system ?? "counter/shelf", 15) + col(s, 16) + n); } const byClass = (s) => STEP.filter((a) => classify(a)[0] === s); let longestRun = 0, streak = 0; for (const a of STEP) { streak = classify(a)[0] === "automatic" ? streak + 1 : 0; longestRun = Math.max(longestRun, streak); } const actor = (a) => a.system ?? "counter/shelf"; const stepOwner = (a) => (a.system ? owner(a.system) : "branch-management"); const handoff = STEP.slice(1).filter((a, i) => actor(a) !== actor(STEP[i])); const ownerCrossing = STEP.slice(1).filter((a, i) => stepOwner(a) !== stepOwner(STEP[i])); console.log(`\n${STEP.length} steps: ${byClass("automatic").length} automatic, ` + `${byClass("human-decision").length} human decision, ${byClass("non-system").length} non-system`); console.log(`longest unbroken automatic run: ${longestRun} steps`); console.log(`handoffs: ${handoff.length}, ${ownerCrossing.length} of which change owner; ` + `the process passes through ${new Set(STEP.map(actor)).size} actors and ${new Set(STEP.map(stepOwner)).size} owners`); // ---- forcing the boundary: if steps that require human judgment are automated ---- const P = 0.15; // chosen rule: probability of a wrong decision per missing information item const wrongProb = (a) => 1 - (1 - P) ** (a.missing ?? 1); const makeRandom = (t) => { let s = t >>> 0; return () => (s = (s * 1664525 + 1013904223) >>> 0) / 2 ** 32; }; const rand = makeRandom(90210); // seed is visible; the same seed gives the same numbers const N = 200; const humanSteps = byClass("human-decision"); let touches = 0, wrong = 0, rolledBack = 0, fixWrites = 0, crossOwnerFixes = 0; for (let i = 0; i < N; i++) for (const a of humanSteps) { if (rand() >= a.frequency) continue; touches++; if (rand() >= wrongProb(a)) continue; wrong++; rolledBack += a.rollbackSteps; fixWrites += a.fix.length; if (new Set(a.fix.map(owner)).size > 1) crossOwnerFixes++; } console.log(`\n${N} loan transactions, seed 90210, wrong-decision probability per item ${P}`); console.log(col("boundary", 18) + col("human touches", 16) + col("wrong decisions", 17) + col("rolled-back steps", 19) + "fix writes"); console.log("-".repeat(84)); console.log(col("current", 18) + col(touches, 16) + col(0, 17) + col(0, 19) + 0); console.log(col("forced", 18) + col(0, 16) + col(wrong, 17) + col(rolledBack, 19) + fixWrites); console.log(`\nthe forced boundary avoids ${touches} human touches, and produces ${wrong} wrong ` + `decisions in exchange`); console.log(`${crossOwnerFixes} of these ${wrong} rollbacks concern more than one owner`); console.log(`break-even: if a rollback costs more than ${(touches / wrong).toFixed(2)} human ` + `touches, the current boundary wins`); console.log("probability sensitivity (expected value):"); for (const p of [0.1, 0.15, 0.2]) { const expWrong = humanSteps.reduce((t, a) => t + N * a.frequency * (1 - (1 - p) ** (a.missing ?? 1)), 0); const expTouches = humanSteps.reduce((t, a) => t + N * a.frequency, 0); console.log(` p=${p.toFixed(2)} -> expected wrong ${expWrong.toFixed(1)}, ` + `break-even ${(expTouches / expWrong).toFixed(2)} touches`); }
step actor class reason ------------------------------------------------------------------------------------------------------ member scans card kiosk automatic - identity is verified identity automatic - membership status is queried membership automatic - unpaid fee is checked billing automatic - accrued fee is collected at the counter counter/shelf non-system happens outside any system material barcode is scanned kiosk automatic - copy status is queried catalog automatic - transfer is opened for a copy at another branch branch-local human-decision 2 information item(s) in no data asset material's damage degree is recorded loan human-decision 1 information item(s) in no data asset loan period is calculated loan automatic - researcher extension is granted loan human-decision 2 information item(s) in no data asset debt in the legacy record is queried loan human-decision data with no writer: legacy-record loan record is written loan automatic - copy status is updated loan automatic - security tag is deactivated counter/shelf non-system happens outside any system overdue notification is scheduled loan automatic - 16 steps: 10 automatic, 4 human decision, 2 non-system longest unbroken automatic run: 4 steps handoffs: 10, 9 of which change owner; the process passes through 8 actors and 5 owners 200 loan transactions, seed 90210, wrong-decision probability per item 0.15 boundary human touches wrong decisions rolled-back steps fix writes ------------------------------------------------------------------------------------ current 62 0 0 0 forced 0 10 30 27 the forced boundary avoids 62 human touches, and produces 10 wrong decisions in exchange 8 of these 10 rollbacks concern more than one owner break-even: if a rollback costs more than 6.20 human touches, the current boundary wins probability sensitivity (expected value): p=0.10 -> expected wrong 9.1, break-even 6.83 touches p=0.15 -> expected wrong 13.4, break-even 4.63 touches p=0.20 -> expected wrong 17.5, break-even 3.54 touches
Where the Boundary Runs
Ten of the sixteen steps are automatic, four require human judgment, two are outside the system. These three numbers alone give the automation rate but not the boundary’s location. The number that gives the location is the length of the longest unbroken automatic run: four steps. The process runs four steps automatically, then falls to the counter; two more steps run automatically, then it snags on two human decisions. The value of automation is not in the step count but in the length of the unbroken run — every break means a wait, a handoff, and starting over.
At enterprise scale, the number that matters is the handoff. Across sixteen steps the actor changes ten times, and in nine of those ten changes the owner changes too. The process passes through eight actors and five separate owners. A process’s enterprise cost is read from here: technically a sixteen-step flow, organizationally nine boundary crossings.
The reasons behind the four steps requiring human judgment are not the same. In three of them, the information is in no data asset. The fourth is different: querying the debt in the legacy record rests on a data asset, but no system writes that data. Ownerless data comes back inside the process as a human decision — a gap in enterprise architecture turns into a staff step in the process.
The Cost of Forcing the Boundary
The second table runs the same two hundred loan transactions through two boundaries. Under the current boundary, four steps are left to staff; across two hundred transactions these steps occur 62 times and spend 62 human touches. Under the forced boundary, all four are automated; human touches drop to zero, and wrong decisions begin in exchange.
With the generator seeded at 90210, this run produced 10 wrong decisions; since the expected value is 13.4, the number depends on the run and changes if the seed changes. Ten wrong decisions roll back 30 steps, and the fix requires a system write 27 times. The number that matters at enterprise scale is the last one: eight of the ten rollbacks concern more than one owner. The cost of a wrongly automated step is not fixing a single screen, it is two or three separate budget owners reconciling the same record.
The decision is read from here: if reconciling a rollback costs more than 6.20 human touches, the current boundary wins. This number is sensitive to probability — when the probability of a wrong decision per missing information item is 0.10, the break-even rises to 6.83 touches; at 0.20 it falls to 3.54. Forcing the automation boundary is not a matter of courage, it is the comparison of these two numbers: touches avoided and rollbacks produced.
Summary
- Three fields of a process step are measurable: actor, input set, output; the notation’s symbol set is secondary next to these fields.
- The automation boundary is derived from a rule: a step is automatic only if all its inputs are in a data asset that has a writer and no information item falls outside it.
- In the model process, 10 of 16 steps are automatic, 4 require human judgment, 2 are outside the system; the longest unbroken automatic run is 4 steps.
- The process passes through 8 actors and 5 owners; the owner also changes in 9 of the 10 handoffs.
- When the boundary is forced, 62 human touches are avoided; in exchange this run produces 10 wrong decisions, 30 rolled-back steps, and 27 fix writes; 8 of the rollbacks concern more than one owner, and the break-even point is 6.20 touches.
Next Step
This lesson opened up a single process for a single capability and counted which systems that process passes through. 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 — which systems will be touched, what data those systems write, and who reads that data is not known unless it is traced one by one. The next lesson builds a traceability chain between capability, system, and data; it counts the number of systems touched when a capability changes and the points where the chain breaks.
To keep your progress and take notes, Log in
My notes
Log in to take notes.