Chapter 5
The Body Plan
DNA is how a part works. Body plan is where it belongs and what it connects to. Draw the creature before you write the cell.
On this page
DNA makes the cell work. Body plan makes the creature coherent.
The frame
Software has DNA and a body plan.
DNA is the implementation inside each part: methods, data structures, conditionals, queries. Body plan is the organism-level design: what parts exist, where the joints are, what connects to what, and what each part refuses to know.
In plain terms: DNA is how a part works. Body plan is where the part belongs and what it talks to.
The two levels answer different questions.
The body plan answers:
- What talks to what?
- Who owns the decision?
- What is replaceable?
- Where is the boundary?
- What does this object not know?
The DNA answers:
- How is this method implemented?
- What data structure fits here?
- How do we name this?
- How do we simplify this conditional?
Both matter. They are different work. Most small refactors and most of what an AI pair drafts live at the DNA level. The costly failures live at the other one.
The danger
Here is the trap: you can write excellent DNA for the wrong creature.
A class can be clean, tested, short, and idiomatic, and still belong to a bad body plan. "Good code" is not enough. Good code inside the wrong relationships still produces a system that is hard to change.
The failure mode has a name: the second nervous system. A parallel structure grows beside the one that already exists. Two ways to route the same kind of request. Two objects that own the same decision. One convention in most of the codebase and a second, cleaner one in the new corner. Each structure looks fine alone. Together they mean every future reader has to learn both, and every future change has to guess which one is load-bearing.
In review, that means the question is never just "is this class good?" It is "does this class belong to the creature we already have?"
Three ways it happens in practice:
The gamed spec. A unit spec built its object through a convenience constructor and asserted a literal string. A hand-rolled standalone class passed, green across the board, while silently breaking the construction contract the real caller depends on. Clean DNA. Parallel object. Wrong creature. Nothing in the test suite objected, because the test was certifying DNA instead of driving the body plan.
The tempting reuse. A new sales path needed fulfillment, and an existing payment orchestrator already did fulfillment. Reusing it would have dragged payment-provider assumptions into a sale that never touched that provider. The fix was a sibling orchestrator that drives the same downstream services and never calls the old one. Sometimes the second nervous system grows from reusing the wrong part, not from writing a new one.
The better convention. Design diagrams proposed idealized class names for a live app that already handled four commands in one consistent shape. Adopting the prettier names would have forked a second style. Match the repo, not the diagram. Two ways to do the same thing, one "better" and one existing, is worse than one consistent way.
The drawing test
The first design question is not "is this code clean?" It is: can I draw this system?
Boxes for objects. Arrows for messages. That is the whole test.
- If you cannot draw it, the body plan is hidden.
- If you can draw it but the arrows go everywhere, the body plan is tangled.
- If one box has all the arrows, you found the god object.
- If you can swap one box for another without redrawing the rest, the body plan has a good joint.
The drawing is not documentation. It is a diagnostic. You are not drawing to record the design; you are drawing to find out whether a design exists.
Body-plan move vs DNA move
Before any non-trivial change, name which level you are working at.
A DNA move changes how a part works without changing who owns the decision or who talks to whom. Rename a variable. Simplify a conditional. Extract a private method called from one place.
A body-plan move changes ownership, boundaries, collaborators, or the path a request takes. Extract a class. Inject a new collaborator. Move a method to where the data lives. Change what an object is allowed to know.
The label determines the process. DNA moves flow freely. Body-plan moves get surfaced, discussed, and decided deliberately, because they are the moves that reshape the creature. The most dangerous phrase in a diff is "while I was here, I cleaned it up." That sentence usually smuggles a body-plan move inside DNA work.
If you cannot decide which label a change deserves, label it up. Surface it.
Architecture, in the sense of load-bearing decisions, is not a synonym for body plan. Architecture is the load-bearing subset of the body plan. Every architectural decision is a body-plan decision; plenty of body-plan decisions (a new collaborator inside one service, a value object, a moved responsibility) never rise to architectural cost.
Refusal is design
A good object has a job and some principled ignorance. Its refusal list is what it declines to know. An object without a refusal list is not designed yet; it is merely named.
Refusal shows up at every scale. The same move, four rungs:
Product. A review-assistant product defined itself by one refusal: it suggests, never enforces. No approval writes, no merge gates, no reviewer assignment. That refusal is not brand tone; it constrains every object downstream. Build one enforcing object and you have built a different, heavier product.
Object. An app-wide callback endpoint was named for its function, not its owner, because the handler's refusal list demanded it: it is a switchboard that routes by payload type and refuses to know what any interaction means. The meaning lives in the handlers it dispatches to.
Collaborator. A service that reaches for the clock and the environment inline (Time.now, ENV[...]) knows things it never declared. Inject the clock. Inject the config. When a domain spec needs a freeze-time helper to pass, that is the object confessing a hidden dependency; injection removes the confession.
Adapter. An adapter consumed exactly the fields the domain needed from a sprawling upstream payload and stored the raw input separately. The refusal at the boundary is what keeps upstream drift out of the domain.
Where the refusal is visible, you get a joint: a designed, visible connection point where the body plan intends one part to connect to another. Constructor injection is the common form, but not the only one; a route, an adapter boundary, a policy object, an event handler can each be a joint. A joint is drawn on purpose. (Feathers' seam, a substitution point you find rather than design, is a different word for a different question; see the Canon.)
The payoff question, and the fastest review question in this chapter: "What does this object refuse to know?" If no one can answer, the object's scope is undefined and it will accumulate every nearby responsibility.
One more honesty rule while you are naming things: when a refusal guard protects against a hazard, say whether the hazard is latent or live. A hazard reachable only through legacy or raw paths is latent; guarding it is defensive hardening, not an incident fix. Framing it honestly changes the urgency, the testing, and the story you tell the team.
When refusal goes wrong
"Refusal is design" gets abused. Three bad refusals to watch for:
The god-object escalation. An object refuses every decision by pushing them all upward, until the caller decides everything. You have not designed an object; you have relocated the god object.
The hidden global. An object "refuses to know" about configuration by reaching for ENV inside a method. That is not refusal; that is a dependency hidden from the drawing. Refusing to know something means declining it visibly, at a joint.
The anemic forwarder. A tiny object that only forwards calls and adds no boundary. It refuses everything, decides nothing, and earns nothing. Deleting it makes the drawing simpler, which tells you it was never a real part.
A good refusal looks like this: accept a collaborator through a visible joint, and decline to know how it does its work.
What proves the body walks
Proving is its own discipline, and it has its own chapters. Drive the Body Plan With Tests sorts tests by what each one proves: the body walks, a joint works, two parts fit the same socket, the outward write lands. P-Cubed's Prove phase runs the rockdrill: the walking skeleton, aimed at the contact surface, rehearsing the new boundary before domain logic lands on it.
The pocket card
Before any non-trivial code, run seven questions:
- What level is this, body plan or DNA?
- Can I draw it?
- Where are the joints?
- What owns the decision?
- What does each object refuse to know?
- What proves the body walks?
- Where could the body plan be bypassed?
Question 7 is the high-skill move. The most dangerous failure is not "we forgot the pattern." It is: we followed the pattern in one place and bypassed it somewhere else. The adapter exists and the caller reaches around it. The body grew a second nervous system while every individual rule was followed.
Seven questions. Before the code, not after.
DNA makes the cell work. Body plan makes the creature coherent. Draw the creature before you write the cell.