charles forson

Rules get followed 70 percent of the time. Design for that, not 100.

3 min read

I used to treat a rule that wasn't being followed as a writing problem. Say it more clearly, add an example, move it earlier in the file. Eventually I stopped doing that, because the pattern that kept repeating was "the instruction was a sentence, asking a probabilistic system to guarantee a deterministic outcome, and it was never going to hold at one hundred percent no matter how well it was worded."

The number I actually use now, based on watching this play out repeatedly, is that a written convention gets followed around seventy percent of the time. Not zero, not a failure of the system, just the honest ceiling on what a rule can guarantee by itself. That's a fine number for plenty of things. It's a genuinely dangerous number for the small set of things where a miss doesn't just mean a slightly worse output, it means a stuck pipeline, a leaked secret, or a decision quietly treated as settled when nobody actually confirmed it.

A stricter rule doesn't fix that. Recognising which decisions belong in a different layer entirely does: a hook, something that runs deterministically and either blocks the action or it doesn't, no interpretation involved. I moved three things into hooks specifically after watching the seventy percent number bite: draft-PR enforcement, worktree requirements before any file edit, and a scan for anything that looks like a committed secret. All three share a property that made them obvious hook candidates in hindsight. Getting them wrong wedges something.

The draft-PR case is the clearest example, because the failure mode is almost invisible until it happens to you. My merge pipeline sets a gate to pending on every PR, and only resolves it when a PR gets marked ready for review, specifically because that event guarantees every commit is actually in and the head is complete. If someone opens a PR non-draft instead of draft-then-ready, the gate goes pending and nothing ever fires the event that resolves it. The PR sits stuck forever, and from the outside it looks like a bug in the merge logic, when the actual cause was a single missed step three stages earlier. I had that rule written down clearly for a long time before I turned it into a hook. Writing it down clearly did nothing, because the miss was about the twenty or thirty percent of the time a convention just doesn't get followed regardless of how well it's phrased.

The pattern generalises past hooks, too. My system tracks provisional decisions, calls made for now, pending some piece of evidence that hasn't arrived yet. Left as prose, that kind of decision depends entirely on someone remembering to revisit it once the evidence lands, and in practice nobody does, reliably, across weeks. The fix was a small structured marker attached to the decision itself, naming exactly what evidence would resolve it, checked automatically on a schedule. The marker just makes "revisit this" a fact a machine can check instead of a fact a person has to remember, which is the same move as a hook, applied to a slower-moving failure mode than a blocked merge.

The honest cost of all this is that building the deterministic layer takes real design effort up front, and it's tempting to skip it because the rule, written down once, feels like it solved the problem. It didn't. It just moved the failure from "I forgot" to "I forgot, and there's a paper trail showing I was told." What I've learned to ask now, before writing any new instruction, is a genuinely different question than how do I phrase this clearly. It's: what happens the day this gets missed, and is that consequence something I can live with at a seventy percent hit rate, or does it need to be a hundred. Most instructions can live with seventy. The ones that can't are exactly the ones worth the extra afternoon of building an actual gate instead of writing a better sentence.

Building the same things I write about — see what I'm working on in Projects, or get in touch.