Lesson 08 / 11
Selecting the Diagram Type by Question
Mapping the structure-showing, sequence-showing, state-showing, and deployment-showing diagram types onto a question set: the correct type per question, how many information items are left missing when a question is answered with the wrong type, and how many items each type has to update per code change.
Contents
The previous two lessons measured a single structure: nodes and edges. Views split that structure along its axis of concern, the layered approach along its scale axis. Every question asked had the same shape — what depends on what.
Someone asking later does not always ask in this shape. In the regional library network, someone asking “what order do the calls go in when a loan request comes in” finds no answer by looking at the dependency list: the list says who can call whom, not which one gets called first. The question “what states does a copy pass through” asks for neither dependency nor sequence; it asks for state and transition. These call for different diagram types.
This lesson does not refer to types by a notation name. A type is defined by the information items it can carry and referred to by its function: structure-showing, sequence-showing, state-showing, deployment-showing. Two things are measured: which type answers which question, and how much it costs to keep that type current.
Function Separates the Type
A structure-showing diagram carries participants, the static link between them, the link’s direction, and multiplicity. A sequence-showing diagram carries participants, messages, the order of messages, asynchrony, and conditional branches. A state-showing diagram carries states, transitions, triggers, and guard conditions. A deployment-showing diagram carries nodes, artifacts, placement, and communication paths.
The interesting thing about the lists is how little they overlap: the four lists take up seventeen slots in total but contain sixteen distinct information items — only participant appears in more than one type. Types are not different drawing habits, they are different information sets; that is why they cannot substitute for each other.
The model has three parts. The item lists each type carries are hand-written (VM11). So are the seven diagrams drawn for the network and the code items each one is linked to (VM12); because a deployment-showing diagram is linked to containers and nodes rather than code, its linked-item count is kept low. The question set is eighteen questions (VM13), and a type answers a question only if it carries every item the answer needs (VM14).
// views/type.mjs — MODEL regional library network: diagram types are defined by // function, mapped onto a question set, and their maintenance cost is counted. Not a // real notation or product; item lists and questions are chosen by hand. // TYPE — the information items each diagram type can carry (VM11) const TYPE = { structure: ["participant", "static-link", "direction", "multiplicity"], sequence: ["participant", "message", "sequence", "asynchrony", "conditional-branch"], state: ["state", "transition", "trigger", "guard-condition"], deployment: ["node", "artifact", "placement", "communication-path"], }; const TYPES = Object.keys(TYPE); // DIAGRAM — the diagrams drawn for the network and the code items each is linked to // (VM12). A deployment-showing diagram links to containers and nodes, not code, so its // linked-item count is kept low. const DIAGRAM = { "structure/loan-service": { type: "structure", linked: ["open-loan", "take-return", "extension", "duration-calc", "limit-check", "late-penalty", "reservation-queue", "reservation-notification"] }, "structure/data-schema": { type: "structure", linked: ["loan-table", "penalty-table", "copy-table"] }, "sequence/loan-opening": { type: "sequence", linked: ["open-loan", "duration-calc", "limit-check", "copy-status", "loan-table", "topic-definition"] }, "sequence/reservation-notification": { type: "sequence", linked: ["reservation-queue", "reservation-notification", "queue-write", "queue-read", "message-body"] }, "state/copy": { type: "state", linked: ["copy-status", "open-loan", "take-return", "reservation-queue"] }, "state/penalty": { type: "state", linked: ["penalty-accrual", "late-penalty", "penalty-table"] }, "deployment/network": { type: "deployment", linked: ["sync-job", "subscription"] }, }; // QUESTION — needs: the information items required for an answer (VM13). A type // answers a question only if it carries every needed item (VM14). const QUESTION = [ { id: "U01", text: "what items does the loan core depend on", needs: ["participant", "static-link"] }, { id: "U02", text: "which direction does the dependency go", needs: ["static-link", "direction"] }, { id: "U03", text: "how many loan records can a member have", needs: ["participant", "multiplicity"] }, { id: "U04", text: "which call comes first in the loan-opening flow", needs: ["message", "sequence"] }, { id: "U05", text: "is the notification call asynchronous", needs: ["message", "asynchrony"] }, { id: "U06", text: "where does the flow branch if the limit is exceeded", needs: ["sequence", "conditional-branch"] }, { id: "U07", text: "what states does a copy pass through", needs: ["state", "transition"] }, { id: "U08", text: "which transition does a return trigger", needs: ["transition", "trigger"] }, { id: "U09", text: "can a reserved copy be checked out directly", needs: ["transition", "guard-condition"] }, { id: "U10", text: "which node does the loan service run on", needs: ["artifact", "placement"] }, { id: "U11", text: "what kind of path connects the branch to the center", needs: ["node", "communication-path"] }, { id: "U12", text: "how many separate nodes are there", needs: ["node"] }, { id: "U13", text: "which participants exist in the loan flow", needs: ["participant"] }, { id: "U14", text: "under what condition does penalty accrual stop", needs: ["transition", "guard-condition"] }, { id: "U15", text: "which message triggers the late penalty in which state", needs: ["message", "sequence", "state"] }, { id: "U16", text: "which participant on which node sends which message", needs: ["node", "participant", "message"] }, { id: "U17", text: "in what order is the reservation notification sent", needs: ["message", "sequence"] }, { id: "U18", text: "which artifact is copied to which node", needs: ["artifact", "placement", "node"] }, ]; const col = (s, n) => String(s).padEnd(n); const missing = (t, q) => q.needs.filter((i) => !TYPE[t].includes(i)); const correctTypes = (q) => TYPES.filter((t) => missing(t, q).length === 0); // ---- type table ---- console.log(col("type", 12) + col("info items", 14) + col("diagrams", 10) + "linked items"); console.log("-".repeat(46)); for (const t of TYPES) { const d = Object.values(DIAGRAM).filter((x) => x.type === t); console.log(col(t, 12) + col(TYPE[t].length, 14) + col(d.length, 10) + d.reduce((a, x) => a + x.linked.length, 0)); } // ---- question -> type mapping and the missing information the wrong type leaves ---- console.log("\nquestion needed item(s) correct type nearest wrong type"); console.log("-".repeat(78)); let single = 0, multiple = 0, none = 0, missingTotal = 0; for (const q of QUESTION) { const c = correctTypes(q); c.length === 1 ? single++ : c.length > 1 ? multiple++ : none++; const nearest = TYPES.filter((t) => !c.includes(t)).map((t) => [t, missing(t, q).length]) .sort((a, b) => a[1] - b[1] || a[0].localeCompare(b[0]))[0]; missingTotal += nearest[1]; console.log(col(q.id, 10) + col(q.needs.join("+"), 31) + col(c.length ? c.join(",") : "- none -", 20) + `${nearest[0]} (${nearest[1]} item(s) missing)`); } console.log("-".repeat(78)); console.log(`${QUESTION.length} questions: single correct type ${single}, multiple types ${multiple}, no type ${none}`); console.log(`the four types together answer ${QUESTION.length - none}/${QUESTION.length} questions`); console.log(`had the nearest wrong type been picked, ${missingTotal} items would be missing in total (${(missingTotal / QUESTION.length).toFixed(2)} per question)`); for (const q of QUESTION.filter((x) => correctTypes(x).length === 0)) console.log(` ${q.id}: needs ${q.needs.join("+")} — ` + TYPES.map((t) => `${t} does not carry ${missing(t, q).join("/")}`).join("; ")); // ---- what each type answers on its own ---- console.log(""); for (const t of TYPES) { const y = QUESTION.filter((q) => correctTypes(q).includes(t)); console.log(`${col("only " + t, 18)}answers ${col(`${y.length}/${QUESTION.length}`, 8)}(${y.map((q) => q.id).join(" ")})`); } // ---- maintenance: 150 code changes ---- // WEIGHT — how often each code item changes (VM15); chosen by hand const WEIGHT = { "open-loan": 9, "take-return": 7, extension: 5, "duration-calc": 8, "limit-check": 6, "late-penalty": 8, "reservation-queue": 5, "reservation-notification": 4, "copy-status": 6, "sync-job": 3, "queue-write": 3, "queue-read": 3, "message-body": 4, "penalty-accrual": 5, "topic-definition": 2, subscription: 2, "loan-table": 4, "penalty-table": 3, "copy-table": 3, // code items not linked to any diagram "shelf-location": 4, "external-query": 5, "record-conversion": 4, "create-member": 4, "cache-key": 3, }; const ITEMS = Object.keys(WEIGHT); function prng(seed) { let s = seed >>> 0; return () => (s = (s * 1664525 + 1013904223) >>> 0) / 2 ** 32; } const rand = prng(20903); // seed is visible const totalWeight = ITEMS.reduce((t, i) => t + WEIGHT[i], 0); const pickItem = () => { let r = rand() * totalWeight; for (const i of ITEMS) if ((r -= WEIGHT[i]) < 0) return i; return ITEMS.at(-1); }; const N = 150; const update = Object.fromEntries(TYPES.map((t) => [t, 0])); let untouched = 0; for (let i = 0; i < N; i++) { const item = pickItem(); let touched = false; for (const d of Object.values(DIAGRAM)) if (d.linked.includes(item)) { update[d.type]++; touched = true; } if (!touched) untouched++; } console.log(`\n${N} code changes (seed 20903); ${untouched} changes touch no diagram`); console.log(col("type", 12) + col("items updated", 17) + col("answers", 8) + "maintenance per answer"); console.log("-".repeat(56)); for (const t of TYPES) { const y = QUESTION.filter((q) => correctTypes(q).includes(t)).length; console.log(col(t, 12) + col(update[t], 17) + col(y, 8) + (update[t] / y).toFixed(1)); } const totalUpdate = TYPES.reduce((a, t) => a + update[t], 0); console.log("-".repeat(56)); console.log(col("total", 12) + col(totalUpdate, 17) + col(QUESTION.length - none, 8) + (totalUpdate / (QUESTION.length - none)).toFixed(1));
type info items diagrams linked items ---------------------------------------------- structure 4 2 11 sequence 5 2 11 state 4 2 7 deployment 4 1 2 question needed item(s) correct type nearest wrong type ------------------------------------------------------------------------------ U01 participant+static-link structure sequence (1 item(s) missing) U02 static-link+direction structure deployment (2 item(s) missing) U03 participant+multiplicity structure sequence (1 item(s) missing) U04 message+sequence sequence deployment (2 item(s) missing) U05 message+asynchrony sequence deployment (2 item(s) missing) U06 sequence+conditional-branch sequence deployment (2 item(s) missing) U07 state+transition state deployment (2 item(s) missing) U08 transition+trigger state deployment (2 item(s) missing) U09 transition+guard-condition state deployment (2 item(s) missing) U10 artifact+placement deployment sequence (2 item(s) missing) U11 node+communication-path deployment sequence (2 item(s) missing) U12 node deployment sequence (1 item(s) missing) U13 participant structure,sequence deployment (1 item(s) missing) U14 transition+guard-condition state deployment (2 item(s) missing) U15 message+sequence+state - none - sequence (1 item(s) missing) U16 node+participant+message - none - sequence (1 item(s) missing) U17 message+sequence sequence deployment (2 item(s) missing) U18 artifact+placement+node deployment sequence (3 item(s) missing) ------------------------------------------------------------------------------ 18 questions: single correct type 15, multiple types 1, no type 2 the four types together answer 16/18 questions had the nearest wrong type been picked, 31 items would be missing in total (1.72 per question) U15: needs message+sequence+state — structure does not carry message/sequence/state; sequence does not carry state; state does not carry message/sequence; deployment does not carry message/sequence/state U16: needs node+participant+message — structure does not carry node/message; sequence does not carry node; state does not carry node/participant/message; deployment does not carry participant/message only structure answers 4/18 (U01 U02 U03 U13) only sequence answers 5/18 (U04 U05 U06 U13 U17) only state answers 4/18 (U07 U08 U09 U14) only deployment answers 4/18 (U10 U11 U12 U18) 150 code changes (seed 20903); 24 changes touch no diagram type items updated answers maintenance per answer -------------------------------------------------------- structure 97 4 24.3 sequence 74 5 14.8 state 59 4 14.8 deployment 11 4 2.8 -------------------------------------------------------- total 241 16 15.1
The Correct Type per Question
Fifteen of the eighteen questions have a single correct type. One question is answered by two types at once: U13, which asks which participants exist in the loan flow, can be answered by both the structure-showing and the sequence-showing diagram, because participant is the only item found in more than one type. Two questions are answered by no type.
This is where it becomes clear that choosing a type is not a preference. When asked which call comes first in the loan-opening flow, the sequence-showing diagram is not an option, it is the only option; no other type carries the sequence item. In the same way, only the structure-showing diagram carries multiplicity, only the state-showing diagram carries guard condition, only the deployment-showing diagram carries communication path. When a question is asked, which diagram to draw is not up for debate; the information items the question requires determine the type.
A document that settles for a single type answers between four and five questions. The sequence-showing type has the largest share (5/18), because it is the type with the longest item list. The four together answer sixteen questions.
The Gap the Wrong Type Leaves
The real measure is in the column on the right. For each question, the nearest wrong type that could be picked instead of the correct one is written down, along with the number of items that type does not carry. The total is 31 information items, 1.72 per question.
The number matters for two reasons. First, the wrong type does not leave the question unanswered — it leaves it half-answered. When asked about the loan flow’s first call, the structure-showing diagram lists the participants; the reader sees something, even something relevant, but the sequence information is not there. It is not obvious that the answer found is incomplete; the missing item is invisible precisely because it is missing.
Second, the nearest wrong type is sometimes very near. U15 asks which message triggers the late penalty in which state; the sequence-showing diagram carries two of the three required items and only fails to carry the state item. A single item is missing, but the missing item is half the question. U16 is likewise one item away from the sequence-showing diagram: node.
These two questions are answered by no type, and the reason is the same as in the previous lesson: the question asks for the items of two types to be combined. Tying a runtime message to a state, or tying the participant sending a message to a node, is more than any single type can carry. This is not a gap that can be closed by adding more types; every new type arrives with its own information set, and a gap remains between the sets.
Maintenance Cost
The table below measures this by tying the diagrams to the code. A hundred and fifty code changes are generated; the item selection comes from a hand-written weight table (VM15) and the seed 20903 is visible. A change requires one item update in every diagram linked to the code item that changed.
24 of the 150 changes touch no diagram — part of the code does not appear in any diagram. The remaining changes generate a total of 241 diagram item updates.
The difference between types is large. The structure-showing type asks for 97 updates and answers four questions: 24.3 per answer. The deployment-showing type asks for 11 updates and also answers four questions: 2.8 per answer. The ninefold gap between them comes not from drawing skill but from how often things link to it. A structure-showing diagram is linked directly to code items, and code is the thing that changes most often; a deployment-showing diagram is linked to nodes and containers, and those change rarely.
The rule that follows is this: if a diagram’s value is the number of questions it answers, its cost is the number of items updated per code change, and the ratio between the two varies ninefold from type to type. If a set of documents has to be trimmed, the place to cut is wherever the maintenance per answer is highest — in this model, the structure-showing diagrams. The numbers depend on the model’s inputs; if the linked-item lists were narrowed, the cost would drop, but the questions answered would not change, because the answer comes from the type’s information set, not from the linked-item list.
Summary
- Diagram types are separated not by their notation name but by the information item they carry: the structure-showing type carries static links and multiplicity, the sequence-showing type carries messages and order, the state-showing type carries transitions and guard conditions, the deployment-showing type carries nodes and placement; of sixteen distinct items, only participant is found in more than one type.
- Fifteen of the eighteen questions have a single correct type, one is answered by two types, two are answered by none; the four types together answer 16/18 questions, a single type answers between 4 and 5.
- The wrong type leaves a question half-answered rather than unanswered: had the nearest wrong type been picked, a total of 31 information items would be missing, 1.72 per question. In U15, the sequence-showing type carries two of three items; the one missing item is half the question.
- The two questions no type can answer both ask for the items of two types to be combined; adding a type does not close this gap, it opens a new one between the new sets.
- With a generator seeded at 20903, 24 of 150 code changes touch no diagram; the rest generate 241 item updates. Maintenance per answer is 24.3 for the structure-showing type, 14.8 for the sequence- and state-showing types, 2.8 for the deployment-showing type — the ninefold gap comes from how often things link to it.
Next Step
All three lessons asked questions of the same kind: what something depends on, in what order it is called, where it runs. Every one of their answers sits in the system’s structure, and once you know where to look, a definite answer comes out.
Some of someone’s later questions are not of this kind. Questions like “is the network fast enough at peak hour,” “does checkout stop if a branch disconnects,” “how open is this system to adding a new branch” do not sit in any diagram as a node or an edge. These are not about the system’s structure but about the quality of its behavior, and as stated they cannot be tested: “fast enough” is not a measure. The next lesson turns these questions into scenarios with stimulus, environment, response, and response-measure fields, and measures: how many scenarios have every field filled in, and how many scenarios cannot be tested because their measure was never written down.
To keep your progress and take notes, Log in
My notes
Log in to take notes.