How to read this
Two things were missing from each other: the thinking (the ideas, faithful to the people who found them) and the practice (the moves you run on an agent's code). Here they're one. Each idea runs:
Source→Figure, where a real one exists→Illustration→How to apply
Eight of the sixteen have a genuine figure; the rest carry none rather than an invented one. Nothing here is a principle I made up — the ideas are the originators'; the application is how they bear on directing AI.
You are not learning to write this code; you are learning to direct an agent and judge what it returns. The agent has made construction nearly free, which moves all your leverage to the two ends it can't do: the specification going in, and the judgment coming out. These are the ideas that judgment rests on.
The master review — run on any code an agent hands you
✓ Names speak the domain, not generic CRUD. (Evans, Parnas)
✓ A likely change would stay in one module, not ripple. (Parnas)
✓ Modules are deep: small interface, real work hidden. (Ousterhout)
✓ It's as simple as the problem — the agent didn't add complexity. (Brooks, Hoare)
✓ Concerns aren't braided — one job per part. (Hickey, McIlroy)
✓ Repetition is the same knowledge, not just similar-looking text. (Hunt & Thomas, Metz)
✓ Handles empty / None / huge / malformed input. (Verify)
✓ You can explain every line — you hold the theory, not just the agent. (Naur)
Where most of your judgment lives. How to draw boundaries (Parnas), how to tell a good module from a bad one (Ousterhout), why structure mirrors the org (Conway), and the discipline of small composable parts (McIlroy).
The accepted way to break up a program was to follow its flow — read, then process, then output. Parnas took one small system, a KWIC index, and decomposed it two ways. The second built each module around a decision likely to change — the input format, the storage, the sort — and hid that decision behind a stable interface. When a change came, the flow-based version rippled through every module; the information-hiding version sealed it in one.
Non-technical illustration
A restaurant menu. You order through a stable interface; the kitchen hides how the dish is sourced and cooked, and can change supplier freely. Organise the kitchen by step instead — one station for “all the starters” — and a supplier change forces every station to relearn. The menu that hides the kitchen's decisions is what lets it change.
How to apply — the test
Take a likely change (“clients now come from an API, not a CSV”) and ask: would it stay in one module, or spread? One is the target. Agents almost always split by step — the criterion Parnas wrote against.
Say to the agent
Reorganise so that a change to [the data source] stays inside one module, behind a simple interface.
Ousterhout asks you to picture a module as a rectangle: the top edge is its interface (what you must know to use it), the area is its functionality (what it does). A deep module is tall and narrow — a small interface over a lot of hidden work. A shallow one is wide and short — nearly as much exposed as hidden, so it barely pays for the interface it adds. Thin wrappers and a scatter of tiny classes (“classitis”) are shallow, and shallow is negative value.

The original figure from Ousterhout,
A Philosophy of Software Design — interface is the top edge (cost), functionality is the body (benefit).
source.
Non-technical illustration
A thermostat. One dial — the entire interface — over a great deal of hidden machinery. Set against a control panel of fifty switches that does barely more than the dial: all interface, little depth. You want the dial.
How to apply — the test
For each module, weigh what you must learn to use it against what it does for you. A bad ratio — lots of interface, little benefit — is shallow; collapse it.
Say to the agent
This is over-split into thin classes. Combine into one deeper module with a simpler interface.
Conway argued that any organisation that designs a system produces a design whose structure is a copy of the organisation's communication structure — in his terms a structure-preserving map, a homomorphism. Three teams building a compiler will tend to ship a three-pass compiler. The paper was rejected by the Harvard Business Review; Fred Brooks rescued it in The Mythical Man-Month and gave it its name. MIT and Harvard researchers later found strong support for it under the name “the mirroring hypothesis.”

Conway's Law — small distributed teams tend to produce modular (microservice) architectures; large teams, monoliths. The system mirrors the org.
Non-technical illustration
A committee's report — you can usually tell from the seams which member wrote which section, because the document's structure mirrors who spoke to whom. The org leaks into the artifact, whether or not anyone intended it.
How to apply — and for clients
As a one-person firm building with an agent there's no team to mirror — which is the point: your tool's structure will reflect how you carve the problem, not an org chart. For clients it's a lever: to change a system's architecture you often have to change team boundaries first — the “inverse Conway” move.
McIlroy, who invented the Unix pipe, distilled the Unix philosophy: write programs that do one thing and do it well, and write programs to work together. The power is not in any single tool but in their composition through a shared, simple interface (text streams). Each tool stays small and replaceable; the system's capability emerges from combining them.

UNIX's layered structure — each layer reaches the next only through a narrow, stable interface (system-call, library). Clean seams, composable parts.
Non-technical illustration
A good kitchen keeps single-purpose tools — whisk, peeler, sieve — not one gadget that does all three badly. You compose them to make the dish. Each does one thing well; the meal is the composition.
How to apply
Ask an agent for small composable functions with clean inputs and outputs rather than one monolith. One job per part is easy to test and easy to recombine — the same instinct as Hickey's decomplecting and Ousterhout's deep modules.
The war on complexity — the judgment you'll use most, with agents and with clients. Why no tool removes the hard part (Brooks), what “simple” means (Hickey), where to spend effort (Knuth), and the most common trap in generated code (Metz, Hunt & Thomas).
Brooks borrowed Aristotle. Software's difficulties are essential — inherent in the problem, the interlocking complexity of the concepts — or accidental, attending its production but not inherent: clumsy languages, slow machines, repetition. Progress had attacked the accidental, with real gains; but because the hard part is conceiving and specifying the thing, no single advance yields an order-of-magnitude improvement. The hardest single part, he said, is deciding precisely what to build.
Non-technical illustration
Writing a novel. The accidental difficulties — the typewriter jamming, the ink running out — a word processor removes entirely. The essential one — having something true to say and finding its shape — no tool has ever touched.
How to apply — and for clients
With an agent the construction is nearly free, so your contribution is almost entirely the essence: the spec and the coherence of the design. This is also what you tell clients AI can and can't compress. (The essence-to-AI link is my reading, not Brooks's.)
Hickey separated two words we conflate. Simple is objective — how many concerns are braided together. He went to the root: simplex, one fold; complecti, to braid together. To make simple is to decomplect — un-braid. Easy is relative — “to lie near,” familiar, in reach. They're different axes: a familiar one-liner can be easy yet produce complexity, because it braids concerns; the simple thing may be unfamiliar. We keep choosing easy and pay later, because entanglement is what makes a system hard to change.
Non-technical illustration
A spork braids spoon and fork into one object — easy to grab, and a worse spoon and a worse fork, where you can't improve one without harming the other. The simple choice keeps the spoon a spoon.
How to apply
Agents reach for the easy by default — the one function that fetches and formats and saves. Hickey hands you the exact word for the smell: “this complects X and Y” — far sharper than “this is messy.”
Say to the agent
This complects fetching and formatting. Split by concern — one job per function. And show me the simplest version that works.
Knuth's most-quoted line: premature optimization is the root of all evil. The fuller thought is that we should ignore small efficiencies most of the time, but not pass up the critical few — and find, by measuring, where the time actually goes. Twisting code for speed before you know the hot path buys nothing and costs clarity. (Knuth half-credited Hoare for the line; Hoare didn't recall saying it, so it is standardly Knuth's — a contested attribution.) His other contribution, literate programming, treats a program as writing for humans first.
Non-technical illustration
Polishing a doorknob on a house you haven't finished framing. Real effort, wrong place — and you don't yet know whether that door will even be there. Measure before you polish.
How to apply
Agents sometimes over-engineer for speed or generality unprompted. Ask for the clear version first and optimise only what you've measured. Knuth's literate instinct is your “explain every line” rule in another key: code is for humans to read.
Hunt and Thomas coined DRY: every piece of knowledge must have a single, authoritative representation in a system. The crucial word is knowledge, not text — two pieces of code that merely look alike but encode different things are not a violation, and duplicating them is correct. Metz named the failure on the other side: the wrong abstraction. Someone unifies two similar pieces; requirements diverge; rather than pulling the abstraction apart, people add flags and conditionals so it serves every case; it rots. Her rule: when you're adding parameters to make an abstraction fit a new case, re-inline it — duplicate the code back — then re-abstract along the seam that actually fits. “Duplication is far cheaper than the wrong abstraction.”
Non-technical illustration
A company's address copied into twenty documents — one move, twenty edits, some missed: that is real DRY, one piece of knowledge that wants one home. But two recipes merged into a master recipe full of “if vegetarian skip step 4; if spicy add step 7” serves neither dish — that is the wrong abstraction. Same-looking is not the same as same-knowledge.
How to apply — the most common call in generated code
When an agent “DRYs up” two similar pieces, ask which it is: the same knowledge in two places (give it one home) or two different things that look alike (leave them separate). If the shared function starts growing flags, tell it to inline back. Telling DRY from the wrong abstraction is the judgment you'll make most often.
The shape of thought beneath the rest. Why structure exists at all (Dijkstra), and the oldest method for breaking a problem down (Pólya) — both predating software, both exactly what you do with an agent.
Dijkstra's 1968 letter argued that unrestricted goto makes a program impossible to reason about, and that control flow should be limited to a few well-behaved structures — sequence, selection, iteration — so the static text matches the running process. (The title “Go To Statement Considered Harmful” was added by the editor, Niklaus Wirth.) In 1974 he coined separation of concerns: study one aspect at a time, in isolation, because no one can hold everything at once. His recurring theme — the humble programmer — is that our intellectual powers are limited, so good structure is a concession to that limit, not a luxury.

The three control structures of structured programming — sequence, selection, iteration — that replaced the goto.
Non-technical illustration
A contract with each clause on its own numbered line, versus one run-on paragraph mixing payment, liability, and termination. The structure lets you reason about one concern at a time — that is the whole of it.
How to apply — the deepest why
Structure exists to fit your limited mind, not the machine's capability. When an agent's code overflows what you can hold in your head, that overload is itself the defect (Ousterhout's “cognitive load”). Ask for it restructured until each part fits.
Pólya, a mathematician, set out four phases for solving any problem: understand it, devise a plan, carry out the plan, then look back to check and reflect. Around them, heuristics — solve a simpler related problem, work backwards, find a pattern, draw a figure. It predates computing by decades, but it is exactly how you break a build into tractable pieces.

Pólya's four steps — understand the problem, make a plan, carry it out, look back.
Non-technical illustration
Planning a dinner party. Understand (guests, diets), plan (menu, shopping, timing), carry out (cook), look back (what to change next time). The steps are domain-agnostic; programming is one instance of them.
How to apply
This is your decomposition discipline with an agent — especially “understand the problem” (write the spec) and “look back” (verify). Agents jump straight to “carry out”; you hold the other three. “Devise a plan” is exactly Plan Mode before you let it build.
What a program is about, and what it is. The living understanding (Naur), the domain's language (Evans), the felt rightness of form (Alexander), what objects really were (Kay), and the test of a sound design (Hoare).
A program is not fundamentally the code or its documents — it is a theory held in the minds of those who built it: how it maps to the world, why it's shaped this way, what each part is for. Naur drew on Gilbert Ryle's sense of a theory as knowing-how: those who hold it can answer new questions and extend the program without violating its logic. And it can't be fully written down — so when the team disperses, the program dies; a new team working from the artifact alone damages it with changes that look reasonable but break the unseen logic.
Non-technical illustration
A city. The map, bylaws, and budget are not the city. Residents hold a tacit theory — which street floods, why a rule exists. Hand outsiders only the documents and they wreck things that read fine on paper. A codebase is the same; so is a craft held in a master's hands.
How to apply — the deep reason for one rule
The agent writes the artifact but holds no theory — and neither will you, if you accept code you can't explain. Walking every line is theory-building: reconstructing the why, not just possessing the what. A tool whose theory lives only in the agent is one you don't own. This is why “never accept code you can't explain” — it's Naur, not a tip.
Evans argued that a team — developers and domain experts together — should share one rigorous language, used in conversation and in the code alike. If the business says “policy” and “claim,” the code says Policy and Claim. The model is not a diagram drawn once; it is a living distillation of the domain's knowledge, refined continuously — and the language and the model are the same thing. A model is valid within a bounded context; the same word can mean different things in different contexts.

A domain-driven design modelling process — align, discover, decompose, strategise, connect, organise, define, code.
Non-technical illustration
A ship's crew uses precise shared terms — port, starboard, halyard, leeward. The shared language is the coordination; “the left-ish rope” would founder them. In a business, the model is that shared language made precise enough to act on.
How to apply — the bridge to your advisory work
Give an agent the domain's real words — client, briefing, intervention, engagement — and insist the code use exactly those (the “names speak the domain” check). The model you'd draw on a whiteboard with a client is the model the code should embody.
Alexander, an architect, defined a pattern as a recurring problem-in-context together with the core of a solution you can use a million times without ever doing it the same way twice. Behind the patterns lay a deeper claim: a quality without a name — a property of places that are alive, whole, comfortable — that you recognise but can't fully define. He later tried to name it through properties of living structure (levels of scale, strong centres, boundaries, good shape). Alexander directly inspired software's design patterns and the first wikis.

Alexander's properties of living structure — levels of scale, strong centres, boundaries, good shape, and so on.
Non-technical illustration
A window seat you instinctively want to sit in; a worn doorstep that feels right; a square that feels alive against a sterile plaza. You can't fully say why, but everyone feels it. Alexander spent his life trying to name that rightness so it could be built on purpose.
How to apply
The one most native to your own vocabulary — “quality without a name” is close to a felt sense of wholeness. In code it is the difference between something coherent you can move through and something correct but dead; your aesthetic sensibility is a real instrument here. (Honest caveat: the leap from buildings to “alive” code is interpretive — the patterns lineage is solid; the aliveness claim for software is more contested.)
Kay coined “object-oriented programming,” and his own account is that it was never chiefly about classes and inheritance. The big idea, he has said, is messaging. He regrets the word “object” because it pointed people at the wrong thing; the essence is autonomous parts — like cells, or computers on a network — that keep their internal state hidden and coordinate only by sending messages, deciding at runtime how to respond (late binding). The common class-hierarchy picture of OOP is a drift from his actual idea, which is closer to information hiding plus message-passing.
Non-technical illustration
A city of people who keep their own affairs private and coordinate only by sending letters. No one reaches into another's house to move the furniture; you send a request and they decide how to answer. That autonomy-plus-messaging is Kay's object — not a taxonomy of “is-a.”
How to apply
Mostly fluency for advising: when people argue “OOP versus functional,” knowing OOP's real root (messaging, encapsulation) cuts through the noise. And it reinforces Parnas — Kay's instinct and information hiding are the same: protect internal decisions, talk through a clean surface.
Hoare drew a line between two kinds of design: one so simple that any flaw would be obvious, and one so intricate that no flaw is — and insisted the first is far harder to achieve, and the only trustworthy kind. Complexity that merely hides problems is not the same as soundness. (Hoare also gave us quicksort, and called the null reference his “billion-dollar mistake.”)
Non-technical illustration
Two arguments. One is so clear that any hole would show — you can see it is sound. The other is so convoluted that no hole is obvious — which is not soundness, only camouflage. The first is much harder to write and much more trustworthy.
How to apply
The standard you hold against an agent's output: not “can I find a bug?” but “is this simple enough that a bug would be obvious?” Complexity that only hides problems is the failure mode — the meeting point of Ousterhout's complexity and Hickey's simple.
Prove it works — and is right. Output is probabilistic, so you verify rather than trust. The one figure here is real and shared across the industry.
An LLM can write clean code that does the wrong thing because it misread the spec. So the pass, in order: right vs works (compare output to the spec, case by case — “it ran” is a different question); edge cases (empty / None / huge / malformed — where generated code is thinnest); invariants (what must always hold — can the code reach a forbidden state?); tests (do they check behaviour, not assert True?).

The test pyramid — many fast unit tests at the base, fewer integration, a few slow end-to-end at the top.
Wikimedia Commons, Abbe98, CC BY-SA 4.0.
Say to the agent
Write tests in a pyramid — mostly fast unit tests, a few integration, one or two end-to-end — covering empty, missing, and malformed input. Then run them and show me the output.
Keep this one by hand
Debugging. Research finds people who lean on AI debug worse over time — they stop forming hypotheses. Reproduce small, read the error bottom-up, print the intermediate values, bisect. The skill is isolating cause, not guessing fixes.
The one-pager. The phrasebook and the vocabulary — for reference, not explanation.
Phrasebook — what to tell the agent
Before it builds
Restate this spec in your own words and list every ambiguity you'd otherwise guess on. Then build only the thinnest end-to-end slice first.
On structure (Parnas, Ousterhout, McIlroy)
Reorganise so a change to [X] stays in one module. Combine thin classes into one deeper module; keep functions to one job.
On simplicity (Hickey, Metz, Hoare)
This complects two concerns — split them. If this shared function needs flags, inline it back. Show me the simplest version.
On the domain (Evans)
Use the language of the problem — client, briefing, intervention — in the names, not generic terms.
On verification
Write and run tests for the edge cases — empty, missing, malformed — and show me the output.
Always (Naur)
Walk me through this line by line; I won't accept code I can't explain.
The vocabulary
Information hiding
Each module hides a decision likely to change, behind a stable interface. Parnas.
Deep module
Much hidden behind a small interface. The opposite of a thin pass-through. Ousterhout.
Conway's Law
A system's structure copies the communication structure of the org that builds it.
Do one thing well
Small single-purpose tools, composed through a clean shared interface. Unix / McIlroy.
Essence vs accident
Complexity in the problem vs complexity we added. Tools remove the second only. Brooks.
Complect / decomplect
To braid concerns together / to un-braid them. Simple = one fold. Hickey.
Premature optimization
Twisting code for speed before measuring where the time goes. Knuth.
DRY
One authoritative home per piece of knowledge — not per similar-looking text. Hunt & Thomas.
The wrong abstraction
A unification of cases that later diverge; duplication is cheaper. Metz.
Separation of concerns
Study one aspect at a time; structure to fit a limited mind. Dijkstra.
Theory building
The program is the living understanding in people's minds; code is its shadow. Naur.
Ubiquitous language
One precise language shared by experts and code; the model is the language. Evans.
Quality without a name
The felt wholeness of a living structure; the root of patterns. Alexander.
Messaging
Autonomous parts that hide state and coordinate by messages — the real idea of OOP. Kay.
Invariant
A property that must always hold, whatever the input. Where correctness is decided.
Edge case
Input at the boundary — empty, zero, max, null, malformed. Where bugs live.