The Shape of the System
Browse<br>ForewordManifestoGlossaryGuidesPosts<br>The manifesto<br>PreambleILocality of reasoningIIMake the data flow explicitIIIParse, don't validateIVEverything across a trust boundary is hostile until proven otherwiseVKeep the responsive path free of uncontrolled-latency workVIEvery wait across an uncontrolled boundary has a deadlineVIIBound what callers can createVIIITear down what you set up; subscribe for latencyIXCheck-then-act on shared state is a race unless it's atomic or serialisedXMake operations idempotent so "do it again" is always safeXISeparate the irreversible decision from its effectXIIFinish your obligations before you exitXIIIFailure modes must be visibleXIVOne source of truthXVName the boundary, version the contractXVILeast privilegeXVIIMeasureXVIIIMake it observableXIXDegrade in tiersXXOptimise for reversibilityXXISimplicity is the budget that funds everything elseXXIIName to revealXXIIITime is an input that lies; measure with a monotonic clockXXIVMake the run reproducibleXXVProcess is structure when code structure runs outThe through-lineAppendix<br>The Shape of the System<br>Structure over vigilance: Engineering for bounded cognition
New here? Start at the front door. Five ways in, depending on why you came.<br>Software gets read far more than it gets written. It gets changed more than it gets read, and then it runs in a world that fails partially, at the worst possible moment, usually while you're asleep. So build correctness into the shape of the system, not into the vigilance of the people keeping it going. Any rule you can only enforce by remembering it is a rule that will eventually get forgotten, and any defence you have to run by hand gets skipped the first time there's a deadline. The objective has two terms and you're meant to push on both of them at the same time: minimise what a tired engineer has to hold in their head to make a correct change, while keeping the blast radius bounded for anything an attacker or an unlucky caller controls. Most of the tenets here buy you that first term. The ones that add code and state to contain failure are paying for the second. When two tenets pull against each other, and sooner or later they will, you don't resolve it with a slogan. It's a judgement about who controls the input and how wide the blast radius is. If the input is caller- or attacker-controlled and the blast radius is wide, pay the cost now. If it's self-controlled and contained, you can defer it, but write down why. These tenets work on one part and one boundary at a time, because that's about all a bounded mind can keep hold of at once. The failures that only turn up once the whole thing is assembled, the ones that belong to no single part, get their own page, The Shape of the Whole. Every tenet below names the failure it stops, the tension it sets up, and one question to ask yourself mid-edit. None of them is free.
ILocality of reasoning over global cleverness<br>A reader should be able to verify a piece of code correct by looking only at that piece and its declared inputs.<br>When understanding one function means you also have to know the call order of three others, and a flag set in a constructor, and some global that gets mutated somewhere else, then you haven't really written a function. You've written a puzzle and scattered the pieces across the repo. Action-at-a-distance is the biggest tax there is on changing code, because before the reader can touch anything they have to rebuild all that invisible context in their head first. Locality is the thing that lets people make correct changes while staying ignorant of 99% of the system.<br>Frontend: a component that derives everything from its props and local state vs one whose behaviour depends on a useEffect three files away mutating shared context.<br>Backend: a handler that reads request-scoped, explicitly-passed config vs one reaching into a process-global singleton mutated by middleware.<br>Data/ML: a stage that declares its input schema and emits a new dataset vs one that mutates a shared dataframe in place, so stage 7's correctness depends on stage 2's side effects.<br>Tension: DRY and single source of truth (XIV) push back here. Inlining everything so each unit stands on its own ends up duplicating logic that then drifts apart. Resolve it by scope: keep control flow and invariants local, but let named, owned shared facts live somewhere else as long as the dependency is explicit (II). Duplicating a three-line guard is cheaper than some spooky-action abstraction, and the wrong abstraction costs you more than the copy does, but duplicating the authoritative definition of a fact is exactly (XIV)'s bug. Copy the things that are only coincidentally similar and will drift apart anyway. Unify only what's genuinely the same fact and has to change together.
Ask yourself: To convince myself this code is correct, how far from this screen do I have to look?
Lineage: local reasoning / the frame rule [O'Hearn, Reynolds & Yang...