5.3 How Delegation Changed

This chapter illustrates

✓ The Modeling Principle · ✓ The Alignment Principle · ✓ The Governed Engineering Environment · ✓ Residual Human Judgment

As the engineered environment matured, the division of engineering labor changed with it.

A finished architecture can make its structures look inevitable. Their histories show which were designed cleanly, which were forced by failure, which required later hardening, and what each allowed the engineer to stop doing by hand.

5.3.1 The Product Path

A user uploads a file. The front door authenticates the request, meters quota, and queues the job; a dispatcher divides the document into chunks; the remediation core applies deterministic rules or bounded model calls through structured document representations; the check engine evaluates remaining findings against accessibility standards; a fidelity validator looks for content lost while accessibility improved; and provenance records what changed. The corrected artifact returns with the evidence those mechanisms produced. Figure 5.3-1 gives the path.

What got built — one upload crosses six responsibilities to a stamped result A left-to-right pipeline. A user uploads a document at the left. It flows through six responsibilities in order: a front door that authenticates the user, meters the quota, and enqueues the job; a dispatcher that splits the document into work units so a large deck finishes in about a minute; a remediation core that, for each fix, either applies a deterministic rule or asks a bounded model; a check engine that maps each remaining finding to a clause of the accessibility standard; a fidelity validator that catches a file that came out more accessible but less true; and a provenance layer that stamps every insertion. The corrected file, with evidence, returns at the right. The one amber stage uses a bounded model call; every other stage is deterministic code that owns the boundaries. Upload a doc Front door auth · quota Dispatcher split units Remediation rule / model Check engine to standard Fidelity content kept Provenance stamp inserts Corrected file + evidence amber — bounded model call rest — deterministic code
Figure 5.3-1. What Got Built. One upload crosses six product responsibilities and returns with standards findings, fidelity evidence, and provenance. Probabilistic interpretation is bounded inside the remediation core; deterministic machinery owns the surrounding workflow and the checks it can actually decide.

That is the product path. The larger engineered environment around it — the subject of the previous chapter — contains the models, tests, lints, orchestration, and other machinery that made this degree of delegation possible.

The architecture narrows probabilistic work into a small action surface. Typed document models own mutation; the lower-level format libraries sit behind those models; deterministic machinery owns workflow and the mechanically decidable checks; fidelity validation covers properties visible only after a transformation has run.

5.3.2 The Model as a Bounded Subroutine

MAGE applies at two scales in this case. Coding agents act on the engineering system; frontier models also run inside the product. The second case is smaller and easier to bound: treat the model as a probabilistic subroutine inside a deterministic workflow. Deterministic code decides when interpretation is needed, packages a bounded input, requires a typed output, and evaluates the candidate against obligations the environment can decide mechanically.

The model's answer is therefore a candidate, not a verdict. A schema can reject malformed output; a standards rule can reject a mechanically invalid claim; a fidelity check can catch some classes of destructive transformation. None of those proves that an alt-text description perfectly captures the meaning of a figure. The trust boundary is only as strong as the obligations the validator can actually evaluate. Properties the checks cannot decide remain probabilistic or require human judgment. Figure 5.3-2 draws the caller, the typed contract, and the validator.

The architecture resembles proof checking in one limited respect: a powerful producer is separated from the smaller mechanism that decides whether its evidence is sufficient for admission 11. Talia Ringer et al., “QED at Large: A Survey of Engineering of Formally Verified Software,” Foundations and Trends in Programming Languages 5, nos. 2–3 (2019): 102–281, https://arxiv.org/abs/2003.06458..

The LLM as a typed function call A left-to-right pipeline in four stages. A deterministic caller owns the workflow and decides when a step needs judgment. It packs the task into a typed input contract and hands it to the probabilistic component, the model, drawn as a dashed box to mark that its output is not trusted. The model returns a candidate answer that must conform to a typed output contract. A deterministic validator then checks that candidate: on a pass, the result is incorporated and control returns to the caller; on a fail, the caller retries or falls back. The trust boundary sits at the validator, not at the model. The dashed model box and the solid caller and validator boxes together say the same thing the prose does: guidance aims, machinery holds. Deterministic caller owns the workflow; decides each step typed input contract Model the probabilistic “function” — one bounded task typed output contract Deterministic validator checks the candidate trust boundary pass result incorporated fail → caller retries or falls back The model is called like a function: a typed task in, a typed answer out — never trusted until a deterministic check has passed it.
Figure 5.3-2. Model as a Bounded Subroutine. A deterministic caller supplies a typed task; the model returns a candidate; deterministic checks reject covered failures before incorporation. Properties outside those checks remain outside the guarantee.

The coding agents that built DocAble pose the same trust-boundary problem at a larger grain. There the engineered environment needs richer system representations, authority across several boundaries, independent evidence, and governance conversion. The bounded subroutine applies the same engineering stance inside the product that MAGE applies to the process that builds it.

5.3.3 Two Paths to Durable Structure

Not every consequential structure was born from failure. The backend became reactive and stateless early: work moved through queues, workers carried no durable truth, and a fan-in step assembled the result. That choice later made the migration from a cluster that billed around the clock to a serverless carrier much cheaper than it might have been; the business logic largely survived while the deployment carrier changed. Likewise, the document editor routed both human gestures and automated remediation through one closed edit vocabulary over the same document representation. That seam later supported a second producer without creating a second mutation path. These were dividends of prior structure, not evidence that every useful abstraction must first be purchased by an incident.

Other seams were different. Their histories begin with a concrete failure and let us trace the response in layers. Those histories expose something the finished boundaries do not: the sequence that produced them.

The computations stabilized before their composition

I made one useful decision about DocAble early, and came to a second only much later. The remediation core was organized as passes: identifiable units of computation, each responsible for some part of analyzing or changing a document. That decomposition proved durable. When we later built a static computation graph, the pass registry handed us the nodes almost for free. I had started from the computation side, and that is precisely why node projection was possible at all.

What I had not modeled nearly as well was their composition. I had not required every pass to declare, in one common vocabulary, what information it produced, what it consumed to make its output, what it read only to decide whether it should run, and how bounded its mutations were. Those facts lived in the code, not in one explicit engineering model.

I do not think the lesson is that I should have specified all of this on day one. Early in the system's development we were still learning what a remediation pass even was: which responsibilities belonged together, and which variations mattered. Some freedom was useful precisely because we did not yet know what deserved to be fixed as structure. A premature composition model might just as easily have encoded the wrong abstractions.

Composition eventually became consequential

The balance changed as the pipeline grew. A pass makes a convenient local boundary, but a loose composition contract admits many locally reasonable implementations: walk the document again, recover state another pass already computed, read a shared structure directly, mutate in place, or add one more special case. Across many passes, those choices began to interact. In one region, work whose local complexity looked harmless accumulated into O(N²)-like behavior across an O(N)-pass pipeline, because passes kept rediscovering or retraversing state; elsewhere, passes mutated the document through different mechanisms. The freedom that had supported exploration was becoming expensive to reason about.

The gap was never a total absence of structure around mutation. By this point the PDF path already captured a typed vocabulary of per-session document edits: roughly thirty edit variants, recorded in order and replayable deterministically. I had modeled meaningful computations, and we could record meaningful mutations. What stayed under-modeled was the relationship between the two — how those computations composed, and which of their effects crossed the boundaries between them.

The model exposed the distinctions that mattered

By then the way the passes composed had become too complicated to understand comfortably from the implementation alone, and the computation graph gave us a way to reduce it. Its first version contained 113 nodes and two edges. The nodes reflected a decomposition that had stabilized; the two hand-authored edges plainly did not capture the relationships among them. So we began deriving the missing composition from the passes themselves, and the first sweep found thirty-three candidate producer–consumer edges.

Trying to derive those relations raised a sharper question: consumer in what sense? Roughly two-thirds of those relationships were not data flow at all. A pass read a signal or a verdict only inside routing logic, to decide whether it should run. Treating those as ordinary data edges would have buried the real data graph in control noise. So the passes acquired a distinction they had not expressed uniformly: they now declare Produces, Consumes, and ConsumesForControl, and the graph projects two relations from them, DATA_FLOW and CONTROL_GATE. The data graph shrank to ten data-flow edges plus one cross-service payload edge; twenty-two control dependencies stayed visible, but separately.

The same exercise exposed a second hidden choice. A pass could produce a bounded typed patch, edit document state directly, or make no change at all. We classified all 68 pass sites on that axis: 15 typed-patch producers, 40 direct editors, 13 read-only. The count did not say the 40 direct editors were wrong. It showed where degrees of freedom remained.

This changed how I read the earlier freedom. Some of it had been productive exploration; we could not have known every useful distinction before building the system. But by the time composition was producing recurring global cost, leaving those relationships implicit no longer bought the same flexibility. And building the model helped show which structure should replace some of that freedom. We had not begun with DATA_FLOW, CONTROL_GATE, and the mutation kinds as a finished ontology. We found those distinctions by trying to represent the system well enough to reason about it.

Figure 5.3-3 reads top to bottom as a partial model growing the half it had been missing.

The modeling history A top-to-bottom modeling history. DocAble began with a stable decomposition of remediation computations, so their node projection was meaningful, while their composition stayed implicit — carried by repeated traversal, shared-state reads, direct mutation, and special cases. As that composition accumulated global cost, modeling the relations exposed two previously implicit dimensions, shown as parallel columns: dependency semantics (DATA_FLOW, CONTROL_GATE, CROSS_SERVICE) and effect boundedness (TypedPatchProducer, DirectEditor, ReadOnly). Typed declarations then made the relationships projectable, a projected graph held by a blocking parity check, with residual freedom remaining. The progression runs from a partial model to a richer one, not from no model to model. The modeling history Stable computations pass decomposition — the nodes already project Composition stays implicit repeated traversal · shared-state reads · direct mutation · special cases Composition becomes consequential locally reasonable choices accumulate global cost model the relations two previously implicit dimensions Dependency semantics DATA_FLOW CONTROL_GATE CROSS_SERVICE Effect boundedness TypedPatchProducer DirectEditor ReadOnly Typed declarations each pass declares its IO facets and its effect kind Projected graph + blocking parity edges projected from the declarations; disagreement fails the build Residual freedom remains not every choice is determinized From a partial model to a richer one — not from no model to model.
Figure 5.3-3. The modeling history. DocAble acquired a stable decomposition of remediation computations before it acquired an explicit model of their composition. As locally reasonable choices accumulated consequential variation, modeling the composition exposed two previously implicit dimensions: dependency semantics and effect boundedness. Typed declarations then made those relationships projectable and mechanically checkable. The progression is from a partial model to a richer one, not from no model to model.

The final move was to stop maintaining those relationships as another hand-written truth. The pass declarations now carry the typed IO, the graph projects its edges from them, and a blocking parity check fails if the declarations and the projected graph disagree.

Freedom can be exploratory

The episode changed how I think about degrees of freedom. Leaving a choice open is often economically sensible: if several realizations satisfy every known obligation, pinning one of them spends effort and can make later change harder. But freedom can also be exploratory. Early in a design the engineer may not yet know which choices are consequential enough to constrain, and variation is what supplies the evidence.

The engineering question is when that freedom has done its job. One signal is recurring consequential variation: independent choices start producing global cost, failures, coordination burden, or behavior no one can follow locally. Another is complexity itself. Even before the right obligations are clear, a region can grow too tangled to explore without a representation. There, modeling precedes specification: build enough structure to reason about the territory, use the model to find the distinctions that matter, and give authority only to the obligations the exploration has revealed.

That describes what happened here better than saying we should simply have modeled more, sooner. The remediation computations stabilized before their composition semantics did. Once that composition became both consequential and hard to reason about, the computation graph helped us find which relationships deserved names and which implementation choices could stay free.

The work is unfinished, and the residuals are part of the lesson. Forty passes remain direct editors. Office-format edges are not yet projected the way PDF edges are, so that check stays audit-only. The graph is static: runtime invocation, duration, retries, cost, and model fingerprints require runtime representations beyond the computation graph. We already record typed per-session edits and can replay them deterministically, but we do not yet join that realized history to the graph's computation identities — an edit names a PDF structure element, not a computation node.

The first place those two representations deliberately touch is testing. The typed IO says where a per-node test begins, and deterministic replay removes model variance from the realized side. We could compose them for that question only because we had kept them orthogonal until a question needed them joined, rather than fusing them early.

Those residuals matter. The point of modeling was never to drive every degree of freedom to zero. It was to know where the freedom is.

The format seam. An early path used a convenient scripting-language library to slice a document. The operation worked visually and silently destroyed the internal tag structure carrying the accessibility the product existed to add. The failure exposed two missing things: an architectural decision — one canonical implementation should own mutation of that format — and environmental authority capable of keeping other paths from bypassing it.

The response came in layers, and Figure 5.3-4 draws the climb. A policy named the canonical library. A typed format model and API concentrated mutation through one seam. A build-time check refused raw access from elsewhere. Then the check itself failed: a stale repository root caused real findings to be reported as green. That second-order incident forced the gate to be hardened too. The finished structural model describes the seam; the incident history explains how it acquired that shape. The model stated the boundary; the API and check gave selected obligations over that boundary authority. The authority itself still had to be maintained.

The format seam, hardened in layers — each layer forced by a residual failure An engineering history, not a system model. A vertical chain of six beats. A convenient call, chosen because it was one line, silently corrupted the document by stripping its accessibility structure — a failure the eye cannot catch. Each response added a layer: a library policy naming one canonical library per format; a typed seam all mutation must pass through; a build-time ban on raw access; and finally the gate's own integrity, after the ban check itself was caught mis-reporting. The lesson: authority over a seam is maintained in layers, not granted once, and even the enforcement earns its own scrutiny. One seam, hardened in layers — each forced by a residual failure. convenient call chosen because it was one line silent corruption tag tree stripped — the eye can't catch it library policy one canonical library per format typed seam all mutation through one model build-time ban enforced, not remembered gate integrity the check itself was caught mis-reporting An engineering history, not a system model.
Figure 5.3-4. One Seam, Hardened in Layers. A convenient library call silently corrupted the accessibility structure the product exists to add. That failure motivated a canonical-library policy and a typed mutation seam that concentrated every format change in one place. A build-time ban then refused the raw calls that would bypass the seam. Later the enforcement itself misreported success when a stale repository root hid real findings, forcing a repair to the gate. Each layer answers a specific failure rather than a plan drawn in advance.

The service seam. Another failure sent a local file path across a service boundary, where it became meaningless in the callee's process. The replacement client accepted file content but not a local path, and an explicit service graph represented which services could call which. A later human review found a second problem: an optional direct-client path could bypass shared rate limiting, key rotation, failover, and cost tracking entirely. A protection boundary only governs the paths that cross it.

5.3.4 The Delegation Staircase

The five rungs are retrospective names for the changing center of gravity of my work—co-coder, QA, review lead, tech lead, architect—not a prescribed career ladder. What matters is what the engineered environment could carry at each stage.

The staircase has two interacting supports. Alignment moved repeatable authority out of direct human review: deterministic checks, permissions, boundaries, and gates could carry obligations I no longer inspected line by line. Modeling moved system knowledge out of my head: named zones and later explicit models let agents reason over larger engineering questions without reconstructing the whole repository each time. Human judgment moved toward the gaps the environment could not yet represent or decide.

Delegation therefore climbed as far as the engineered environment could carry it. When representation was too weak, I fell back into reconstructing the map. When authority was too weak or unreliable, I fell back into reading diffs and verifying outcomes manually. A miss could send the staircase backward.

Co-coder — direct human inspection carried everything. The first system was small enough that I read the agent's changes, judged them, and merged them. The human supplied both the system context and the admission decision. That worked while the volume stayed inside one person's attention. It failed as an operating model once generation outran review.

QA — evaluation was delegated before authority was. I began dispatching a second agent to audit another agent's work. That removed some reading from my hands but did not make the result authoritative: the audit ran episodically, produced probabilistic findings, and still depended on someone to decide what followed. Its value was diagnostic. Repeated decidable findings revealed questions that no longer needed a language model at all.

Review lead — reviewers specialized by concern, while decidable findings dropped into machinery. One generalist audit over a growing codebase produced too much undifferentiated advice, so review split by concern and by region. Architecture, security, duplication, and naming could be judged in the context where they mattered. When a finding turned out to be mechanically decidable, it became a deterministic check; the probabilistic reviewers retained the questions those checks could not settle.

Tech lead — representation made reasoning local. Specialized reviewers were still finite reasoners confronting an expanding repository. Named zones externalized ownership and architectural boundaries so an agent could reason inside one region without first recovering the whole system. Boundary checks constrained some cross-zone edits. This stage combined the two principles explicitly: the zone model reduced reconstruction; selected rules over that model received authority.

Architect — explicit models carried system-level knowledge. By the final rung I rarely read implementation directly. Architectural, behavioral, ownership, and other models supplied the system views I needed for planning and review; agents used the same representations to localize changes. This did not make the models true by declaration. Model drift became its own failure class, which is why traceability and correspondence checks eventually became part of the environment. When the cost of restructuring collapses, the economics invert — a cross-format migration that touched fifty-eight files and rewrote five thousand lines took about five hours of agent time over a dinner break, and postponing the right shape becomes the expensive choice. The hard part was never touching fifty-eight files; it was knowing that fifty-eight files should be touched.

What explicit models made checkable

Explicit models changed what could be established mechanically. A lifecycle model made illegal transitions checkable. An ownership model reduced a concurrency protocol to the states and interleavings needed to ask whether two workers could hold the same claim or whether a stale holder could clear a newer lease. A small, purpose-built Python model checker exhaustively explored the finite abstract state space and checked the ownership invariants. Selected conformance tests separately exercised the corresponding production operations against the model's predictions.

Other properties called for different machinery. Eventual termination is a property over executions rather than merely reachable bad states, so I wrote small TLA+ specifications and checked them with TLC. The specifications modeled failure and recovery explicitly: when recovery was disabled, TLC produced the stranded execution the property was intended to exclude.

Checking the model created a second obligation: why should a property established over the model be believed about the implementation? DocAble never answered that question with formal refinement or implementation-level model checking. For the ownership protocol, selected conformance tests exercised real production operations against the executable model. For the temporal specifications, the connection remained weaker.

That weaker connection became an engineering problem of its own. One model-to-implementation conformance test drifted red after production changed and remained broken for days. Repairing the test fixed the instance; the durable response addressed the class. Formal specifications were registered behind a common runner, model-to-spec correspondence became mechanically checkable, and changes to modeled production surfaces began selecting the relevant formal and conformance checks. The checks themselves therefore acquired some of the same drift controls as the models they supported.

The distinction matters. Exhaustive checking establishes properties of a model. Correspondence evidence supports the separate claim that the implementation still realizes the relevant parts of that model. Strong evidence for the first claim does not make the second automatic.

Nor did every formal result receive the same authority. The explicit-state ownership checks participate directly in the engineering environment; TLC execution remains advisory rather than a hard release gate. That asymmetry is deliberate. DocAble adopted formal methods where they made a consequential property easier to state or search, then gave the resulting evidence only the authority its connection to the implementation could support. Structural enforcement handled properties that could be made impossible by construction; state exploration handled dangerous interleavings; temporal model checking handled obligations over executions.

The formal machinery mattered, but representation came first. Before the lifecycle and ownership models existed, I could describe the system's behavior in prose, but questions about legal transitions, competing claims, stale ownership, and eventual termination remained slippery. Once those behaviors had names and explicit structure, properties that had been difficult even to formulate became obvious enough to ask—and sometimes mechanical enough to check. The representation did not merely record my understanding of the system. It changed what I could understand about it.

Software engineering has long exploited a milder version of this effect: a shared vocabulary makes recurring structures easier to recognize, reason about, and communicate. This insight is part of what made the "Gang of Four's" Design Patterns so influential. Gamma and colleagues argued that naming a pattern increases the designer's vocabulary, raises the level at which a design can be discussed, and makes it easier to think about designs and their tradeoffs.** The authors make vocabulary an explicit benefit of pattern names: naming recurring structures lets designers reason and communicate at a higher level of abstraction CITE0. Models pushed the same idea further in this case. They supplied not just names but structured semantics: once states, transitions, ownership, and temporal obligations had explicit representations, new properties became available both to human reasoning and to mechanical analysis. The representations available to an engineer shape the properties that engineer can readily see and state.

Field note — the map that pointed at a ghost. One model named a code symbol that was later moved and renamed. Nothing invalidated the reference. The model continued to look authoritative, agents continued to reason from it, and the failure surfaced days later during deployment. That incident forced live trace resolution: model-to-code edges were re-resolved against current symbols so a stale pointer failed at the change that broke it rather than downstream.

Evidence — a reasoning surface creates a maintenance obligation. A later audit showed the pointer failure was a class, not a one-off. Re-running closed work against current code found genuine model-to-code drifts that a green definition of done had missed, the prod-blocking pointer among them. Mechanical correspondence checks then turned stale-reference failure into something the environment catches at the change that creates it. Appendix H carries the counts.

Formalizing and instrumenting previously weakly represented seams also exposed live defects that green tests had not. The useful claim is not that "models find bugs." It is that forcing an engineering relation into an explicit representation creates new questions the environment can ask, and in these three cases those questions exposed shipping defects.

The same instrumentation measured its own reach: a traceability tracer made the remaining unmodeled surface measurable and guided later modeling toward the largest gaps. Appendix H carries the run-by-run series and the discrimination that keeps the number honest.

What modeling exposed that green tests missed. Three live defects came into view when previously implicit relations were represented explicitly:

Figure 5.3-5 draws the five-rung climb.

The delegation staircase — five ascending steps from Co-coder to Architect; delegated work climbs, the engineered environment accumulates, and residual human burden narrows but never vanishes A true staircase of five steps ascending from bottom-left to top-right. Step one at the bottom is Co-coder; step five at the summit is Architect. Each step card names a stage and shows, in blue, what is now delegated, and in green, what the engineered environment now carries. Along the left edge a green gauge thickens as the climb proceeds — representation and authority accumulate. Along the right edge a red trajectory narrows from wide at the base to a sliver at the summit: the residual human focus moves from reading every diff at the bottom to system-level strategy and residual semantic judgment at the top — the tactical burden shrinks but never reaches zero. Step one Co-coder: delegated is code generation; the environment carries nothing durable beyond an ordinary repository; the human reads every diff, holds the map, and admits the change. Step two QA: delegated is generation plus a second agent's audit; the environment carries a probabilistic, episodic, advisory reviewer; the human adjudicates findings. Step three Review lead: delegated is specialized review per region; the environment carries deterministic checks for the decidable findings; the human does the semantic review a check cannot settle. Step four Tech lead: delegated is bounded work inside one modeled zone; the environment carries named zones and boundary rules; the human holds the system map and strategy. Step five Architect: delegated is model-guided work across the whole system; the environment carries explicit models, traceability, and controls; the human is left with residual semantics and strategy. A failure in representation or authority can push work back down the staircase. HUMAN FOCUS strategy · residual semantics environment accumulates representation + authority residual human judgment narrows — never zero 5 · ARCHITECT explicit models carry system-level knowledge delegated — model-guided work across the system environment — explicit models · traceability · controls residual semantics + strategy 4 · TECH LEAD representation makes reasoning local delegated — bounded work inside one modeled zone environment — named zones · boundary rules system map + strategy 3 · REVIEW LEAD semantic residue after deterministic checks delegated — specialized review, per region environment — deterministic checks for the decidable findings semantic review 2 · QA evaluation delegated before authority delegated — generation + a second agent's audit environment — a probabilistic reviewer (episodic, advisory) adjudicate findings 1 · CO-CODER direct human inspection carries everything delegated — code generation environment — nothing durable beyond an ordinary repository read every diff · hold the map · admit the change more work carried by the engineered environment delegation climbs
Figure 5.3-5. The Delegation Staircase. As explicit representation and environmental authority accumulated, larger units of engineering work could be delegated. Representation moved system knowledge out of one person's head; authority moved repeatable admission out of direct review. Human work moved from reading every diff toward system-level strategy and the residual semantic judgment the environment could not yet decide. A failure in either representation or authority could move work back down the staircase.

Delegation increased as the environment took over work I had previously carried in direct review: models carried system knowledge, while mechanisms carried repeatable admission decisions. A weak model pulled me back into reconstructing the map; weak evidence or controls pulled me back into inspecting outcomes. Human judgment stayed at the unresolved edge. Parts II–IV present the clean method that can now be taught directly; this case shows why those pieces exist and why they did not arrive as one package. The next chapter reconstructs the incidents that moved that edge.

Works Cited

  1. Ringer, Talia, Karl Palmskog, Ilya Sergey, Milos Gligoric, and Zachary Tatlock. “QED at Large: A Survey of Engineering of Formally Verified Software.” Foundations and Trends in Programming Languages 5, nos. 2–3 (2019): 102–281. https://arxiv.org/abs/2003.06458.
  2. Gamma, Erich, Richard Helm, Ralph Johnson, and John Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley, 1994.
© James C. Davis, 2026–present