Section 10Steering architecture you don't fully understand

Agents write more code than any one person can hold in their head. The question stops being "do I understand every line?" and becomes "am I steering the shape?" You can, without reading it all — by owning interfaces, not internals.

Section 9 dealt in guardrails you can point to — scanners, hooks, signed contracts. This section's subject is quieter: the shape of the system itself, and how you keep a grip on it as the volume of code leaves you behind. Picture the salon booking app a few months in: bookings, reminders, a calendar sync, payments on the way. That's tens of thousands of lines, most of which no human has read end to end — not a failure, but the normal working state this whole guide assumes. The question is what, in that state, you are still meaningfully in charge of.

Here's the part that surprises people: architecture matters more with agents, not less. When writing code was expensive, sloppy structure was a tax you paid slowly, over years. Now that code is cheap and abundant, structure is the bottleneck — it decides how much context an agent must load before it can safely change anything, whether two agents can build side by side without colliding (Section 11), and whether you can review by looking at boundaries instead of reading every line (Section 6). Three habits do most of the work, and they are this chapter: own the interfaces, make big decisions reviewable, and match your attention to how reversible each change is.

For newcomers

"Architecture" is the floor plan, not the furniture. Software architecture sounds grand, but it answers three homely questions: which rooms does the building have (the modules — bookings, calendar, notifications, payments), what is each room responsible for, and where are the doors between them (the interfaces — the small set of functions one module exposes for others to call). The furniture inside a room can be rearranged any time without the neighbours noticing; move a wall or a door, and everything touching it has to adapt. This chapter is about being the person who owns the walls and doors while the agent rearranges furniture at speed.

Own the interfaces; let the agent own the insides

Build around deep modules (John Ousterhout's idea): a lot of complexity hidden behind a small, stable interface — the public functions and types other code calls, also called its API. You are the strategic programmer — you design that public interface and the boundaries. The agent is the tactical programmer — it fills the implementations. As long as the interface holds, you can steer a system whose insides you didn't write.

Concretely, for the salon app: the bookings module might expose a single call — "book this customer, for this service, at this time; tell me if the slot is taken." That sentence is the interface. Behind it hide the double-booking check, buffer times between appointments, timezone handling, the waiting-list logic — all furniture the agent can rearrange for as long as the sentence stays true. Your job is to write the sentence, keep it stable, and pin it down with blackbox tests at the boundary — tests that call the interface exactly the way other code will, so a full rewrite of the insides either passes them or gets rejected without you reading a line of it.

Steering the internals

You review every function the agent writes inside a module. You're the bottleneck again, and the codebase outgrows you within a week.

Steering the interface

You review the module's public API — the types other code depends on. The agent can rewrite the insides freely; behaviour tests prove it still works.

Deep or shallow — the one question to ask of any module

Ousterhout's book A Philosophy of Software Design gives the sharpest test for whether a boundary is worth owning. A module is deep when it hides a lot of functionality behind a simple interface — like the bookings sentence above. It's shallow when the interface is nearly as complicated as what it hides: to send a reminder you must pass the message template, the provider configuration, the retry policy, and remember to check quiet hours yourself. A shallow module isn't just ugly — it leaks. Every caller has to know its internals, so every internal change ripples outward, and the "you own the interface, the agent owns the insides" deal collapses, because there's no meaningful inside left to delegate.

For newcomers

A deep module is a dishwasher. One door, three buttons — and behind them, plumbing, heaters, pumps, sensors, and a control program you will never see. That's depth: enormous capability, tiny surface. The shallow version of a dishwasher would expose forty valves and expect you to sequence them yourself — technically more "flexible," practically a burden shifted onto every user. Software modules span the same range, and Ousterhout's point is that depth is a choice: the same functionality can be packaged either way, and the packaging — not the cleverness of the insides — decides how hard the system is to work with later.

Depth is also what makes a module a good unit of agent work. A deep module is a small prompt: "here's the interface, here are the boundary tests, make the inside satisfy them" fits in one clean session (Section 12). A shallow one drags half the codebase into the context window before the agent can safely touch it. So when you review a plan (Section 4), this is the structural thing to check — not variable names, but: is each proposed module deep? You can ask the agent to grade itself: "list every module's public interface next to a one-line summary of what it hides; flag any where the interface is nearly as complex as the implementation." That's a review you can do in minutes, on a codebase you've never read.

The salon app's floor plan

Here's what owning interfaces looks like on the running example — four rooms, four one-sentence doors:

Bookings

Interface: book, cancel, or move an appointment; report a conflict. Hides: double-booking rules, buffer times, waiting lists, timezone edge cases.

Calendar sync

Interface: push a booking out, pull external busy-times in. Hides: each provider's API quirks, retries, and what happens when the connection drops mid-sync.

Notifications

Interface: "send this customer this kind of message about this booking." Hides: email versus SMS, templates, quiet hours, delivery retries.

Payments

Interface: charge, refund, report failure. Hides: the entire payment-provider integration — the one room no other module is ever allowed to reach into directly.

Write this floor plan down where the agent can't miss it — the project's AGENTS.md, or an architecture doc it's told to read — with one blunt rule attached: modules talk to each other only through these interfaces. Now "steering the shape" has a concrete meaning. Any change that stays inside a room can run with light oversight; any change to a door shows up in review as exactly that — a changed interface, a short, readable diff of function signatures and types rather than a thousand lines of internals.

Make big decisions reviewable

For consequential choices, ask the agent to design it twice — present two or three options with trade-offs, not one. Record the choice as an ADR. Run a second, adversarial agent to argue against the design. You're reviewing decisions, which are few and high-leverage, instead of lines, which are many and low.

"Design it twice" is Ousterhout's advice too, and agents remove its last excuse. When exploring a second design cost a senior engineer a week, almost nobody did it; when it costs an agent twenty minutes, there's no reason to accept the first idea that compiles. Ask for options that are radically different — not two flavours of the same approach — and insist the trade-offs are stated against your actual requirements from grilling (Section 3): "Option A is simpler but locks us into one calendar provider; Option B costs an extra module and three more days, but a second provider becomes a configuration change." You don't need to be able to invent Option B to be excellent at choosing between A and B — that is the whole trick of steering without reading.

For newcomers

ADRs, part two. Section 3's box covers the format itself — one short file with context, decision, and consequences. What this chapter adds is the provenance and the housekeeping. The idea comes from Michael Nygard's 2011 essay "Documenting Architecture Decisions"; the records are numbered and live in the repository next to the code (a docs/adr/ folder is the convention); and an old one is never rewritten — a new record supersedes it, so the folder reads like the project's decision history. Writing one takes ten minutes; with an agent drafting it from the discussion you just had, closer to two.

ADRs are the agent's long-term memory

ADRs were invented for human teams, but they solve an agent problem almost too neatly to be a coincidence. Section 12 will tell you to work in fresh sessions — which means the agent that starts tomorrow's ticket remembers nothing about why you rejected the simpler schema in March. If that decision lives only in an old chat transcript, it's gone, and a future agent will cheerfully re-propose the thing you rejected, with the same confident reasoning as the first time. If it lives in docs/adr/0007-one-booking-table.md, then "read the ADRs before proposing structural changes" becomes a standing instruction, and the decision keeps binding every future session — human or agent — for the price of a few hundred tokens.

Decision buried in a chat

Three weeks ago, in a long session, you and the agent decided against per-service booking tables. That session is gone. Today a fresh agent proposes per-service booking tables, and its argument sounds great — because it's the same argument you already spent an hour refuting.

Decision recorded as an ADR

The same choice is ADR 0007: context, decision, consequences — ten lines. Every session starts by reading the ADR folder. The fresh agent's proposal now opens with "ADR 0007 rules this out unless we supersede it" — the past argues back on your behalf.

The adversarial half of the habit deserves mechanics too. Run the challenge from a genuinely separate session — a fresh agent, or a subagent, that receives the design document and nothing else, prompted to attack rather than assist: "argue why this design fails at ten times the load, breaks when a booking spans midnight, or paints us into a corner when the salon opens a second location." An agent that helped produce a design is a poor critic of it — agreement is the failure mode of asking the author. What you want is Section 6's review dynamic applied one level up: not "is this code right?" but "is this decision right?" — with the strongest objection, and why you overruled or accepted it, folded into the ADR's consequences section.

Triage by reversibility — one-way doors and two-way doors

The third habit: match your involvement to blast radius — light oversight for a change that's easy to undo, your full attention for one that isn't. The idea has a famous articulation. Jeff Bezos's 2015 letter to Amazon shareholders split decisions into Type 1 — consequential and irreversible or nearly so, "one-way doors," to be made methodically, carefully, slowly — and Type 2: changeable, reversible "two-way doors" that should be decided quickly, close to the work. His warning was that large organisations apply the heavyweight one-way process to everything and grind to a halt. With agents the temptation runs the other way: because code is cheap, everything feels like a two-way door — the agent can always rewrite it, can't it? For code behind a stable interface, mostly yes. But three things stay stubbornly one-way even in an agent-speed codebase, and they're worth knowing on sight:

Data outlives code

The day real bookings exist, the schema stops being a file and becomes thousands of customer records shaped a particular way. Changing it now means migrating live data — rehearsable, but never free, and a botched migration has no undo button.

Published interfaces have users

Once a calendar partner or a customer's browser depends on your public API, changing it breaks code you don't control and can't fix. Inside your repo, the agent can rewrite both sides of a boundary; past your walls, it can only break one.

Auth & identity compound

How accounts, roles, and log-ins work threads through every feature built after it. Swapping that model later means touching all of them at once — the closest thing software has to re-pouring a foundation.

So the triage in practice: a two-way change — copy, a component, anything behind a feature flag — gets a green light and light review, because spending one-way-door ceremony on it is how you lose the speed agents bought you. A one-way door gets this chapter's full stack at once: design it twice, an adversarial review, an ADR — and, cheapest de-risking of all, a disposable prototype or tracer bullet first, because the one moment a hard-to-reverse decision is still fully reversible is before you've committed to it. And fight software entropy here: agents accelerate the drift toward a "ball of mud," so architecture review is active maintenance, not a nicety.

Entropy — why the drift is faster now

The "ball of mud" warning above has a pedigree: Brian Foote and Joseph Yoder's 1997 paper "Big Ball of Mud" named it the most frequently deployed software architecture — a casually structured system whose organisation is "dictated more by expediency than design." That was written about human teams under deadline pressure. An agent is an expediency engine: every ticket gets solved the locally easiest way, and no individual shortcut is exactly wrong — a helper duplicated here, a module quietly reaching around an interface there, because that was the shortest path to green tests. Entropy was always the default; agents simply compress a decade of drift into a quarter.

The counter-move is a standing ritual, not a heroic rescue. On a rhythm — monthly, or after every few shipped features — run an architecture review session: ask the agent to redraw the floor plan from the code as it actually is ("list the modules, their public interfaces, and every place one module reaches around an interface into another's internals") and compare it with the floor plan you wrote down. The difference between intended and actual structure is your entropy reading. Small divergences become refactoring tickets while they're still two-way doors — and done on a rhythm, they always are. Skip it for six months and the ball of mud isn't a risk any more; it's the architecture.

Make architecture review a session type

Turn the ritual into a saved prompt or a skill: redraw the module map, flag interface violations and shallow modules, propose the three smallest refactors with the highest structural payoff — and end, as always, with tickets, not vibes. It costs one session a month and buys a codebase that stays steerable.

There's one more payoff, and it's the next section's whole premise: clean interfaces are what make parallel agents possible. Two agents can build the notifications room and the payments room simultaneously precisely because neither needs to touch the other's walls — the doors are agreed, so the work can't collide. Steering the shape isn't just how you stay in control of one agent; it's the precondition for safely running several.

Quick check: the agent proposes changing the bookings database schema. What's your move?

Recognise the one-way door. Once real bookings exist, a schema change means migrating live customer data — so it gets the full ceremony: two or three genuinely different designs with trade-offs, an adversarial agent arguing against the favourite, a prototype to de-risk the scary part, and the choice recorded as an ADR before implementation starts.

The wider chapter in one breath: with agents you stop steering lines and start steering shape. Own the interfaces — small, stable doors on deep modules that hide their complexity — and let the agent own the insides, with blackbox tests guarding each boundary. Review decisions, not code: design it twice, let a separate agent attack the design, and write the outcome down as an ADR so it survives every fresh session. Triage by reversibility: two-way doors run fast and light; one-way doors — schemas holding live data, published APIs, the auth model — get your full attention while walking away is still cheap. And because agents accelerate entropy, redraw the floor plan on a rhythm and refactor the drift while it's small. The reward comes in Section 11: agreed interfaces are what let several agents build at once without colliding.