Three is a pattern: conventions that survive generated code
An agent will happily write the same function four times in four files, each slightly different, each passing its own test. So will a tired human on a Friday. The volume just went up, so the habit that keeps a codebase coherent has to be written down where anyone, and anything, working in the repo will read it. Here is the one I lean on hardest.
The rule
Two similar things might be coincidence. Three is a pattern, so extract on the second copy.
That is the whole thing, but the timing is the part people get wrong. If you wait until the third copy exists to refactor, you are now reconciling three call sites that have already drifted, and the cleanup is bigger than the feature that spawned it. Extract when you are about to write the second one, while the shape is still fresh and there is only one thing to change.
Before you write anything
The rule only works if you look first, so every non-trivial change starts with the same three questions:
- Will this make the next change harder? Every new file, type, or name is surface area. Fewer moving parts by default.
- Does this already exist? Grep for the shape before you write it, and actually look in the pattern library and the shared packages rather than trusting your memory of what is in there.
- Are you about to duplicate? More than three copied lines, the same class string typed twice, a function shaped like one you touched an hour ago: stop, refactor to something reusable, then write the new caller.
A coder adds the nineteenth argument to make today’s task work. An engineer never lets a function reach nineteen arguments in the first place. The difference is entirely in what you do in the thirty seconds before you type.
Writing the patterns down
A convention that lives only in my head does not survive contact with a second contributor, never mind an agent that starts every task with no memory of the last one. So it does not live in my head. It lives in a directory in the repo called .mex, from the mex project: the conventions, the architecture, the API rules, and a library of patterns, which are short recipes for the things we do more than once. Adding a REST endpoint. A cache-aside facade. An htmx fragment handler. Keyset pagination. Each is a page with the reasoning and a worked example.
The bit that makes it work for agents is a router. Every session, before touching anything, the agent reads .mex/ROUTER.md, and that points it at whichever conventions and patterns the task actually needs. So a fresh run does not start cold and guess my house style, it starts by reading it. Same for a new human, which is the part people forget: the thing that keeps the agents in sync is the same thing that onboards a person.
The promotion rule matches the extraction rule. Once I have written something a third time, it becomes a pattern in .mex, so the fourth run, agent or person, follows the path instead of reinventing it slightly wrong.
Why this matters more with agents, not less
The instinct when output goes up is to relax the bar, on the grounds that most of the diffs are small and mechanical. That is exactly backwards. Volume is what makes a skim feel reasonable and exactly what makes it stop working. The diffs that look mechanical are where the drift hides.
Written conventions are what let the review stay fast without getting looser. If the pattern says how an endpoint is wired, reviewing a new endpoint is checking it against the pattern, not re-deriving the design every time. The agent writes more code; the rules that code has to satisfy did not move an inch. That is the trade I am willing to make, and the only one that holds up.