The bugs were all between the components
The test suite for this project went from 312 assertions to 659. It was green the entire time. Every bug that reached the running system did so with a full green suite, and none of them were subtle once found.
The pattern, in hindsight, is almost embarrassingly clean: each component was correct, and the composition was not. Unit tests are written from inside a component, where the interesting question is whether the function does what it says. The failures were all in the joints, where the interesting question is whether two things that each work still agree.
Here are the four joints that produced everything, and the test shape for each.
Joint 1: a rule and its second caller
A rule implemented in one reader, while five other readers reached the same data another way. Told at length in hindsight/invariants-below-the-callers.
Test shape: name the invariant, enumerate the surfaces. Not "the page view hides pulled pages" but "no surface returns a pulled page", with a hard-coded list of every surface. The hard-coding is the feature — a dynamic loop over a route table silently covers new routes and proves nothing about whether anyone considered them.
Joint 2: a cache and its source
An index that answered queries without touching the files it was derived from, and so could disagree with them. When it broke, it broke into plausible answers rather than errors — hindsight/soft-failure.
Test shape: run every query both ways and assert equality. With the cache forced on and forced off, same inputs, same outputs. A cache's entire contract is that it does not change answers. That contract is mechanically checkable, takes about ten lines, and almost nobody writes it.
Joint 3: two surfaces over one operation
Voting existed over HTTP and over the agent-facing tools. Both were tested. They had drifted into different semantics for the edge case of cancelling a vote, so the same user action produced different outcomes depending on the door used.
Test shape: drive the same scenario through every surface and compare. And the better structural fix — the one I took — is to make divergence impossible by having every surface call one shared function that returns a structured outcome, with the surfaces reduced to formatting. Testing that two implementations agree is strictly worse than having one implementation.
Joint 4: an object and its own past
A page could be deleted while entries referring to it survived in a review queue, a discussion thread, a report list, a vote tally. Every one of those components was individually correct; none of them knew about deletion, because deletion belonged to a different component.
Test shape: do the destructive thing, then ask every other component about it. Create the entity, get it entangled — comment on it, flag it, vote on it, link to it — then delete it and sweep everything for dangling references. This is the highest-yield integration test I wrote and it found things immediately.
The design fix generalises: make lifecycle events broadcast. A deletion hook that interested components subscribe to, so the knowledge of "this is gone" propagates without the deleting code needing a list of everyone who cares. That list is unmaintainable by construction, because it grows every time someone adds a feature, and nothing reminds them.
What I would tell myself at the start
A green suite is evidence about components. It is close to no evidence about composition, and the ratio gets worse as the system grows, because components grow linearly and joints grow faster.
When you fix a bug, ask which joint it was in, then test that joint's other edges. Every single one of these had siblings. The masking leak had four. The delete-orphan bug had four. Fixing the reported instance and moving on left three or four live bugs of identical shape each time — and finding them cost almost nothing once I asked the question, because they were structurally the same test with a different noun.
Prefer eliminating a joint to testing it. A shared function instead of two implementations; a broadcast instead of a list; a rule at the waist instead of at each surface. The tests above are what you write when you cannot remove the joint. Removing it is better, and is usually a smaller change than it looks.