Skip to content
academia.sh

Lesson 04 / 11

The Architect and Developer Relationship

Modeling the ivory tower problem as a knowledge gap: keeping the import graph the decision-maker knows separate from the actual graph, running the hands-on and hands-off schemes on the same sequence of changes, and counting the stillborn-decision rate, the average knowledge gap, and the feedback delay.

Contents

The previous lesson’s oversight scheme silently assumed one thing: that the person who writes the constraints, counts the violations, and reverts them can actually see the import graph. This assumption does not hold in every scheme. If the person making the decision never touches the code, a gap accumulates between the graph at the moment they make the decision and the graph months later.

Ivory tower is the name for this gap. It is usually described as a personality flaw — an attitude that talks from a distance, disconnected from implementation. In this lesson it is taken not as a flaw but as a measurable quantity: the edge difference between the graph the decision-maker knows and the actual graph. As the gap grows, it becomes possible to count how many decisions are already infeasible the moment they are published.

The Stillborn Decision

A decision is stillborn when it forbids a state that has already come to exist. In the library network model this takes a concrete form: the architect says “the staff front must not look directly at branch inventory”; but that edge was added three months ago, and the architect does not know it. The decision is announced, the team says the decision is already violated, the decision is withdrawn. The decision does not have to be wrong — being late is enough.

The model builds this as follows. Two graphs are kept: the actual graph and the graph the architect knows. Developers add a shortcut every round; a shortcut is a direct edge drawn to a module that is already reached indirectly. The architect makes one decision every round and chooses it by looking only at the graph they know. The decision counts as stillborn if the edge it selects already exists in the actual graph (WA10).

Two schemes are compared. The hands-on architect works on the code every round and sees 95% of the added edges. The hands-off architect gets a report every six rounds, and 70% of the edges appear in the report; the rest never appear at all (WA11). Both schemes get the same sequence of shortcuts; the sequence is generated once and its seed is written down.

Measurement

// architecture/knowledge-gap.mjs — the gap between the graph the decision-maker knows and the actual graph
// IMPORTS is the MODEL library network graph from the previous lessons; it is given again so the block runs on its own.
const IMPORTS = {
  configuration: [], clock: [], logging: ["configuration"],
  "data-access": ["configuration", "logging"], "event-bus": ["configuration", "logging"],
  authentication: ["data-access", "configuration", "logging"], authorization: ["authentication"],
  "catalog-connector": ["configuration", "logging"],
  "catalog-cache": ["catalog-connector", "clock"],
  "loan-rule": ["configuration", "clock"],
  "loan-core": ["loan-rule", "data-access", "event-bus", "clock", "catalog-connector"],
  reservation: ["loan-core", "notification-queue", "data-access"],
  "member-registry": ["data-access", "logging"],
  "member-penalty": ["member-registry", "loan-rule", "clock", "data-access"],
  "branch-inventory": ["data-access", "event-bus"],
  "branch-sync": ["branch-inventory", "event-bus", "catalog-connector", "logging"],
  "notification-queue": ["data-access", "event-bus"],
  "notification-email": ["notification-queue", "configuration"],
  "report-daily": ["data-access", "clock"],
  "report-batch": ["data-access", "branch-inventory", "clock", "logging"],
  "web-front": ["loan-core", "catalog-cache", "member-registry", "reservation", "authentication"],
  "staff-front": ["loan-core", "member-registry", "member-penalty", "branch-inventory", "report-daily",
    "authentication", "authorization"],
  "kiosk-front": ["loan-core", "catalog-cache", "authentication"],
  "batch-job": ["report-batch", "branch-sync", "notification-queue", "notification-email"],
};
const MODULES = Object.keys(IMPORTS);

function prng(seed) { let s = seed >>> 0; return () => (s = (s * 1664525 + 1013904223) >>> 0) / 2 ** 32; }
const copy = (g) => Object.fromEntries(MODULES.map((m) => [m, [...g[m]]]));
const reachable = (g, a, b) => {
  const seen = new Set([a]), stack = [a];
  while (stack.length) for (const h of g[stack.pop()]) if (!seen.has(h)) { seen.add(h); stack.push(h); }
  return seen.has(b);
};
const shortcuts = (g) => {
  const c = [];
  for (const a of MODULES) for (const b of MODULES)
    if (a !== b && !g[a].includes(b) && reachable(g, a, b)) c.push([a, b]);
  return c;
};
const edgeCount = (g) => MODULES.reduce((t, m) => t + g[m].length, 0);

const ROUNDS = 18, PER_ROUND = 3, SEED = 31337;
const base = copy(IMPORTS);
const pool = shortcuts(base);
const draw = prng(SEED);
const remaining = [...pool], SEQUENCE = [];
for (let i = 0; i < ROUNDS * PER_ROUND; i++) SEQUENCE.push(remaining.splice(Math.floor(draw() * remaining.length), 1)[0]);
console.log(`base graph ${edgeCount(base)} edges; ${ROUNDS} rounds add ${SEQUENCE.length} shortcuts (seed ${SEED})`);

// scheme: visibility = the share of actual edges seen at each refresh, refreshEvery = how many rounds between refreshes
function run(visibility, refreshEvery) {
  const actual = copy(base), known = copy(base);
  const rand = prng(SEED + 1);          // the decision draw and the visibility roll, from the same seed
  const addedAtRound = new Map();
  let stillborn = 0, gapTotal = 0;
  const delays = [];
  for (let t = 0; t < ROUNDS; t++) {
    const c = shortcuts(known);         // the architect looks only at the graph they know
    const [a, b] = c[Math.floor(rand() * c.length)];
    if (actual[a].includes(b)) stillborn++;  // the forbidden edge already exists in reality: the decision is stillborn
    for (const [x, y] of SEQUENCE.slice(t * PER_ROUND, (t + 1) * PER_ROUND)) {
      actual[x].push(y); addedAtRound.set(`${x}>${y}`, t);
    }
    gapTotal += edgeCount(actual) - edgeCount(known);
    if ((t + 1) % refreshEvery !== 0) continue;
    for (const x of MODULES) for (const y of actual[x])
      if (!known[x].includes(y) && rand() < visibility) {
        known[x].push(y); delays.push(t - addedAtRound.get(`${x}>${y}`));
      }
  }
  const unseen = edgeCount(actual) - edgeCount(known);
  return { stillborn, avgGap: gapTotal / ROUNDS, unseen,
    avgDelay: delays.length ? delays.reduce((a, b) => a + b, 0) / delays.length : 0 };
}

const col = (s, n) => String(s).padEnd(n);
const handsOn = run(0.95, 1);   // hands-on architect: every round, 95% of the edges
const handsOff = run(0.7, 6);   // hands-off architect: once every six rounds, 70% of the edges
console.log(`\n${col("scheme", 26) + col("stillborn", 12) + col("avg. knowledge gap", 21) + col("unseen edges", 15)}avg. delay (rounds)`);
console.log("-".repeat(93));
for (const [name, r] of [["hands-on architect", handsOn], ["hands-off architect", handsOff]])
  console.log(col(name, 26) + col(`${r.stillborn}/${ROUNDS}`, 12) + col(r.avgGap.toFixed(1), 21) +
    col(r.unseen, 15) + r.avgDelay.toFixed(2));

console.log(`\nfeasibility rate: hands-on ${(((ROUNDS - handsOn.stillborn) / ROUNDS) * 100).toFixed(0)}%, hands-off ${(((ROUNDS - handsOff.stillborn) / ROUNDS) * 100).toFixed(0)}%`);
console.log(`stillborn-decision gap = ${handsOff.stillborn - handsOn.stillborn} decisions; knowledge gap ${(handsOff.avgGap / Math.max(handsOn.avgGap, 0.1)).toFixed(1)} times`);
console.log(`delay gap = ${(handsOff.avgDelay - handsOn.avgDelay).toFixed(2)} rounds; edges the hands-off architect never sees at all = ${handsOff.unseen}`);
base graph 64 edges; 18 rounds add 54 shortcuts (seed 31337)

scheme                    stillborn   avg. knowledge gap   unseen edges   avg. delay (rounds)
---------------------------------------------------------------------------------------------
hands-on architect        0/18        3.1                  1              0.02
hands-off architect       8/18        13.8                 9              3.40

feasibility rate: hands-on 100%, hands-off 56%
stillborn-decision gap = 8 decisions; knowledge gap 4.5 times
delay gap = 3.38 rounds; edges the hands-off architect never sees at all = 9

Reading the Three Numbers

Feasibility rate. Eight of the hands-off architect’s eighteen decisions forbid a state that has already come to exist by the time they are published: a rate of 56%. In this run, the hands-on architect produced no stillborn decisions at all, a rate of 100%. The second number being exactly a hundred is not the model’s guarantee — that architect also had a one-edge blind spot, and none of the eighteen draws happened to land on that edge; with a different seed, one or two stillborn decisions could turn up. The direction of the difference, though, is independent of the seed: information fed by a report arrives later and more incomplete than information fed by the code.

Knowledge gap. The average edge difference between the actual graph and the known graph is 3.1 for the hands-on architect, 13.8 for the hands-off architect — 4.5 times. The 3.1 for the hands-on architect is not zero, because the edges added within a round come after that round’s decision; this is a lag that no scheme can bring to zero. The 13.8 for the hands-off architect, though, accumulates: the edges added between reports pile up on top of each other.

Unseen edges. This is the harshest number of the three. The number of edges the hands-off architect never sees at all by the end of eighteen rounds is 9. These are not delayed information but information that never arrives; a report is a summary, and every edge left outside the summary stays permanently invisible. The basis for the next decision is not a 64 + 54 = 118-edge graph, but a graph missing nine edges.

Feedback delay. The average number of rounds between an edge being added and reaching the decision-maker is 0.02 for the hands-on architect, 3.40 for the hands-off architect. This is the average delay of a six-round report interval. The delay is not just a wait: every decision made across those three and a half rounds rests on a stale graph, and that is exactly what feeds the stillborn-decision rate.

Closing the Gap

The measure’s practical consequence is that the architect touching the code is not a preference but a channel for refreshing knowledge. The gap depends on two variables: refresh frequency and refresh coverage. Getting a report once every six rounds lowers the frequency; the report carrying only 70% of the edges lowers the coverage. Fixing only one of the two is not enough — if frequency goes up while coverage stays low, unseen edges keep accumulating.

Two workable paths follow from this, and both are measurable. The first is raising coverage: producing the graph a decision rests on directly from the code rather than from a report — the previous lesson’s constraint audit did exactly this, and the 24-hour oversight load measured there is, at the same time, the load that closes the knowledge gap. The second is raising frequency: the architect reading the modules a decision touches before making it, that is, making the decision by looking at that part of the graph.

The ivory tower’s problem is not that the decisions are bad. Ten of the hands-off architect’s decisions were feasible; eight lost their value because they came late. The same person is deciding by the same criteria; the only thing that changes is the age of the graph they have on hand at the moment of the decision.

Summary

  • Ivory tower is not an attitude but the measurable knowledge gap between the graph the decision-maker knows and the actual graph.
  • On the same 54-shortcut sequence of changes, eight of the hands-off architect’s eighteen decisions were stillborn (56% feasibility); the hands-on architect produced no stillborn decisions in this run.
  • The average knowledge gap is 3.1 edges against 13.8 edges, a factor of 4.5; the hands-on architect’s 3.1 is a within-round lag that no scheme can bring to zero.
  • The number of edges the hands-off architect never sees by the end of eighteen rounds is 9; a summary report produces not a delay but a permanent blind spot.
  • Feedback delay is 0.02 rounds against 3.40 rounds; what changes is not the quality of the decision but the age of the graph at the moment of the decision.

Next Step

This lesson measured the conditions under which a decision is born: the fresher the graph the decision-maker sees, the more feasible the decision. A decision being feasible, though, does not mean it gets applied. The oversight measurement in the lesson before this one showed that decisions lose their counterpart in the code; that measurement never distinguished how the decision reached the team. Yet the same decision can be announced as a rule, written down with its rationale, or conveyed by working together during implementation. The next lesson runs the same decision through three forms of conveyance and counts two things: the correct-application rate and the cost of conveyance — how many hours, how many people.

To keep your progress and take notes, Log in

My notes

Log in to take notes.

Start typing to search.

↑↓ Esc navigate · open · close