Skip to content

Antipatterns of Omission

The first two antipatterns were things that happen. These three are things that were never built.

Nobody made a mistake. A control that would have caught the problem simply does not exist, usually because it produced no visible feature and therefore never won a prioritisation argument. That makes these the antipatterns an architect is most responsible for, because they are the ones with no natural advocate.

graph TD
  A["<b>No evaluation</b><br/><i>cannot answer:<br/>is it better than last month?</i>"] --> D["Shipped on<br/>an impression"]
  B["<b>No bounds</b><br/><i>no step cap, token budget,<br/>timeout or spend cap</i>"] --> E["One conversation<br/>visible in the bill"]
  C["<b>No attribution</b><br/><i>cannot say which<br/>component grew</i>"] --> F["Spend climbs with<br/>no decision behind it"]

  classDef gap fill:#D8D3CB,stroke:#B8B2A8,stroke-width:2px,color:#3F3F3F
  classDef cons fill:#D9695B,stroke:#B4483A,stroke-width:2px,color:#ffffff
  class A,B,C gap
  class D,E,F cons

Three missing controls and what each permits. The drained boxes are absences — which is why none of them appears in an incident review as a cause.

What it is. The system ships with no systematic measurement of output quality. It seemed to work in testing, and it continues to seem to work.

Why it happens. Evaluations cost money per run and human time to curate, and they produce no feature. Under deadline pressure they are the easiest thing to defer, and deferring them has no immediate consequence — which is exactly what makes them easy to defer again.

Detection signal. Ask: is it better or worse than last month? If nobody can answer with a number, the deployment is eval-free regardless of what the plan says.

Structural fix. Module 6’s Section 3. The minimum viable version is small — a frozen set of real cases, a grader that handles the structurally checkable properties, and a reported number with variance — and a small eval that runs is worth far more than a comprehensive one that was never built.

What it is. A loop with no step cap, no token budget, no wall-clock timeout and no spend ceiling.

Why it happens. It works fine in development, where the tasks are short and the operator is watching. Bounds only matter on the pathological interaction, which by definition has not happened yet.

Detection signal. Cost variance with a long tail; interactions taking minutes; and the specific tell — a single conversation visible as a line item in the monthly bill.

Structural fix. Module 5’s four bounds, with Module 6’s arithmetic as the argument: because cost grows with roughly the square of turn count, an unbounded loop is not linearly expensive, it is quadratically expensive. And Module 7’s retry topic adds the sharper risk: an unbounded loop that can retry consequential actions is not just a cost problem.

What it is. Spend climbing steadily with no single decision responsible. A prompt grows a paragraph at a time. Retrieval depth creeps up because someone was chasing a quality problem. A cache quietly stopped working after a prompt-assembly change. Three tool schemas were added.

Why it happens. Each individual change is small and defensible, nobody is looking at the aggregate, and the bill arrives monthly while the changes arrive daily.

Detection signal. Cost per resolved task rising while nobody can name a change that caused it. The giveaway is the phrase nothing changed — which here means nothing changed that anyone attributed.

Structural fix. Module 6’s governance: attribution per traffic class, published routinely rather than when something goes wrong, and the one-lever-per-change discipline so effects attribute. Plus the cheapest single check — verify the cache is still being read, from usage data, after every prompt-assembly change.

All three are prioritisation failures wearing engineering clothes, and all three share a structure: the control produces no visible feature, so it has no advocate.

A feature has a product manager who wants it. A spend cap has nobody. An eval set has nobody. A step cap has nobody until the afternoon it would have saved you.

Which is precisely the architect’s job as Module 2 framed it: these are requirements with owners, written down, in the NFR budget, with names attached. An unowned control is a control that will be cut, and the moment it gets cut is the moment somebody needs to ship by Friday.

Three more entries for your antipattern review, and the honest version of this exercise is to check which of the three your own design merely mentions versus actually assigns. A control named in a document with no owner is closer to absent than present.


→ Three absences. The next antipattern is the opposite: reaching for something before anyone said what was wrong. — The “just add RAG” reflex