Skip to content
The Operon Library

Volume IX · Chapter 9

Reusable Thinking

Frameworks, patterns, and the economics of reuse.2026-07-13 · 8 min read

A four-person team asks an agent to add notification support to a signup flow: an email when a new account is created, with a one-line aside that SMS or push “might come later.” The agent does not ask what “might” means. It returns a NotificationChannel interface, an EmailNotificationChannel that implements it, a NotificationFactory that selects a channel from configuration, and a small registry so that, in the agent’s own summary comment, “any future channel can be added by implementing one interface.” The codebase has exactly one caller of any of it: the signup handler, which needs to send exactly one email.

The reviewer reads it, sees clean names, a passing test suite, and the unmistakable shape of code a competent engineer writes when they can already see three requirements coming. It merges in an afternoon. Two months later product actually asks for SMS, and the abstraction turns out to be built around assumptions specific to the one channel anyone had implemented — synchronous delivery, a single recipient, a return value no SMS provider’s API would recognize. Adding the second channel means reworking the interface anyway, so the team ends up touching more code than a second plain function would ever have needed. The generality bought nothing. It was scaffolding built for a shape nobody had actually seen yet.

What the review missed

The failure here is not that the agent wrote bad code — by most conventional measures it wrote good code: well named, internally consistent, exercised by tests. The failure is that it applied a real, well-known pattern to a problem that did not yet have the shape the pattern assumes. Rigorous research aimed squarely at this specific failure — an agent choosing a familiar pattern on surface resemblance rather than genuine structural fit — is still thin. The closest direct evidence comes from a 2025 study assessing whether code-generation models recognize, comprehend, and correctly produce established design patterns, which found that models regularly fail to capture a project’s actual coding standards and generate code that conflicts with the patterns a codebase already uses, leaving developers to reconcile the mismatch by hand. Trade coverage of production AI-assisted codebases in 2026 describes the same shape from the outside, in plainer language: code that, in one account, “over-allocates, over-abstracts, duplicates logic, and misses subtle optimization opportunities” relative to what the task in front of it actually needed.

Generating a pattern costs an agent almost nothing extra. Recognizing whether the pattern belongs here was always the only step worth paying for.

The judgment nobody had to name before

This Library’s Volume VIII chapter on complexity management already drew a distinction worth reusing here rather than rederiving: essential complexity belongs to the problem itself and does not disappear no matter how the solution is expressed, while accidental complexity is friction introduced by the tools and process used to build it. Writing the boilerplate for a factory-and-interface pattern is close to pure accidental complexity — syntax, wiring, the tax of expressing an idea in a language’s idioms — and it is exactly the kind of thing an agent erases almost for free. Deciding whether a problem actually has the shape that pattern assumes — genuinely divergent behavior behind a shared interface, more than one instance already in evidence, a real reason the divergence will recur — is essential complexity. It is a judgment about the problem, not a fact about the code, and it does not get easier just because the code that follows from it got cheaper to produce.

Before agents, this judgment was rarely made explicit, because the two steps were fused inside one person’s effort. An engineer about to spend two hours hand-writing a factory and three subclasses had a built-in check on over-application: the tedium. Reaching for a heavier pattern cost something, so engineers tended to reserve it for cases they were fairly confident would pay off. Generation cost is now close to zero, and the check tedium used to provide is gone with it — nothing stops a plausible-looking abstraction from being produced for a case that will never justify it, because producing it was never the expensive part.

An agent that generates a design pattern fluently has demonstrated that it can type. It has not demonstrated that it understood the problem in front of it.

On the judgment step design patterns were always meant to compress

Patterns as compressed vocabulary, not code templates

It is worth being precise about what the Gang of Four actually argued, because the popular version of their work flattens into “a catalog of code you can copy.” In the 1993 conference paper that preceded their 1994 book, Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides described design patterns as providing “a common vocabulary for designers to communicate, document, and explore design alternatives” — the naming was the point, not the code underneath it. They went further: “a good name is vital, as it will become part of the design vocabulary,” because experienced designers rely on a large body of accumulated design experience, and a name is how that experience moves from one person’s head into a form a whole team can share without each person re-deriving it from scratch.

That is what makes a pattern valuable even when the literal code never gets reused across two projects. Two engineers who agree “this is a Strategy” have compressed a paragraph of reasoning about polymorphism and interchangeability into three words, and every future conversation about that code can build on the compression instead of re-explaining it. The compression only holds, though, if the label is accurate. A team that calls something a Strategy pattern when it is really three copies of similar logic with no shared invariant has not compressed anything — it has attached a trusted name to something that does not deserve the trust, and the next person who reads the label will reason from it as though it were true.

What reuse actually costs

Reuse is not free, and the cost is not hypothetical — it is the generality itself. An abstraction meant to serve more than one call site has to be built to handle cases none of today’s call sites actually have, and that extra surface is a form of complexity even when it is well written. Sandi Metz’s widely cited 2016 essay on “the wrong abstraction” names the failure precisely: an abstraction built before its shape is settled tends to accumulate parameters and conditional branches as new requirements arrive that do not quite fit it, until the code meant to reduce duplication becomes harder to read than the duplication ever was. Her argument that “duplication is far cheaper than the wrong abstraction,” and that the fastest way out of a bad one is usually to inline it and start over, is a direct claim about reuse economics: the abstraction has to earn its generality, and until it does, the plain, repetitive version is the cheaper asset to own.

Software engineering has a long-standing informal check for exactly this, popularized in Martin Fowler’s Refactoring and attributed to Don Roberts: wait until a shape has appeared three times before extracting it, because two instances are not enough evidence that a third will look the same. Fowler makes a related point under the banner of YAGNI — you aren’t gonna need it — arguing that the comparison has to weigh the cost of building a capability now against the cost of delaying something needed sooner, discounted by the real probability the anticipated case never arrives at all. Both heuristics lean on a signal an agent structurally lacks unless a harness supplies it: the felt sense, built from having typed something similar recently, of whether a shape has actually recurred or is being imagined into existence.

The long-known failure on the other side of the same coin is what the 1998 book AntiPatterns named the Golden Hammer: a team fluent in one solution reaches for it regardless of whether the new problem fits. The AI-era version is not quite the same shape. A model has not spent years mastering one tool the way a person or a team can; it has been exposed to thousands of patterns in roughly equal measure, so the risk is not narrowness of experience — it is that the pattern chosen is whichever one the problem’s surface features most resemble in training data, which is a statement about textual similarity, not structural fit.

A short test before the pattern lands

None of this argues against ever using a named pattern in agent-generated code — plenty of problems genuinely have the shape a pattern assumes, and naming it correctly is still the fastest way for a team to reason about it together. It argues for treating pattern selection as a decision that gets checked, the way the code implementing it already gets checked, rather than accepted on the strength of how fluent the result reads.

Question to ask before mergingFavors keeping the abstractionFavors inlining it back
How many concrete instances of this shape exist right now?Three or more, already in the codebaseZero or one — the rest are imagined
Do the instances share real behavior, or just similar-looking code?A genuine shared invariantCoincidental resemblance, likely to diverge
Can the pattern be named and justified in one sentence?“This is a Strategy because X varies independently of Y”The justification restates what the code does, not why
What happens when the next instance does not fit?The interface extends cleanlyA parameter or conditional gets bolted on
Who pays the abstraction’s ongoing complexity cost?A team that will reuse it repeatedlyWhoever has to read it once, alone

Where the signal would need to live

None of the five questions above require exotic instrumentation — they require tracking what already exists in version control over a longer window than most review processes currently look. A pattern’s call-site count at merge time is knowable. So is the same count ninety days later, and so is whether the abstraction was later inlined back to a single caller, which is the plainest possible signal that its generality was purchased before it was needed.

What to check on Monday

  1. Ask what problem-shape evidence justified the pattern, not just whether the code compiles. Three real call sites is evidence; one imagined future call site is a guess wearing a diagram.
  2. Make pattern selection part of review, explicitly. If nobody reviewing a diff can say in one sentence why this problem has the shape the pattern assumes, the review checked the implementation, not the judgment underneath it.
  3. Watch for interfaces that get reworked, not just extended, the first time a second real case arrives. That is not a wasted afternoon — it is data that the abstraction was built before its shape was actually known.
  4. Let duplication survive on purpose sometimes. Two similar-looking pieces of code sitting side by side, unabstracted, are not technical debt if a third instance genuinely has not shown up yet — they are the evidence a good abstraction will eventually be built from.

None of this is an argument for avoiding design patterns in agent-authored code, any more than Volume VIII’s argument was against using agents on genuinely complex features. It is an argument against mistaking a pattern’s fluency for evidence of its fit. The next chapter in this volume turns to the harder half of the same problem: the judgment that never gets written down as a named pattern at all, and how it moves from one engineer to the next anyway.

For Discussion

  1. The last abstraction your team built for a case that had appeared only once — has it since been reused, reworked, or quietly inlined back to where it started?
  2. When an agent proposes a named design pattern in a diff, does your review process ask why the pattern fits, or only whether the resulting code passes tests?
  3. How many places in your codebase currently carry the complexity of a general solution to serve exactly one caller?

References

  1. establishedDesign patterns as a common vocabulary for design communication, not just reusable code — the argument underlying the 1994 bookErich Gamma, Richard Helm, Ralph Johnson & John Vlissides, "Design Patterns: Abstraction and Reuse of Object-Oriented Design," ECOOP '93 · 1993
  2. establishedPremature abstraction: duplication is often cheaper than the wrong abstraction, and the fastest way out is to inline and rebuildSandi Metz, "The Wrong Abstraction" · 2016-01-20
  3. establishedYAGNI: weighing the cost of building capability now against the cost of delay and the odds it is never neededMartin Fowler, "Yagni" (bliki) · 2015-05-26
  4. establishedThe Rule of Three: wait for a genuine third occurrence of a shape before extracting an abstraction (attributed to Don Roberts)Martin Fowler, Refactoring: Improving the Design of Existing Code · 1999
  5. establishedThe Golden Hammer antipattern: a team fluent in one solution applies it regardless of whether a new problem actually fitsWilliam J. Brown, Raphael C. Malveau, Hays W. McCormick III & Thomas J. Mowbray, AntiPatterns: Refactoring Software, Architectures, and Projects in Crisis · 1998
  6. emergingCode LLMs often fail to capture a project’s existing design patterns and generate code that conflicts with them, across recognition, comprehension, and generation tasksPan, Song, Wang, Cao, Li, Li & Liu, "Do Code LLMs Understand Design Patterns?" (arXiv preprint) · 2025-01-08
  7. emergingProduction AI-generated code described as routinely over-allocating, over-abstracting, and duplicating logic relative to task needDavid Linthicum, "The AI coding hangover," InfoWorld · 2026-03-13