321 Oak
Process

Exceptions Don't Prove the Rule

A small design problem from an AI-assisted build, and the working habit it produced: when a rule blocks something legitimate, ask what the rule protects before you write an exception.

A laptop screen showing several VS Code windows side by side, each with a Claude Code conversation open.
Yes. Yes, you do see Claude in four different tabs on that screen. The crazy thing is, I cleaned it up for the picture. I actually have 8 VS Code windows active right now.
TL;DR

When a standard blocks something that has to happen, the quick fix is an exception, but exceptions admit the rule was wrong without correcting it. Ask what the rule protects, restate it around that aim, and keep it checkable. Then write the lesson into your process so the next problem starts from the better habit.

An exception may be telling you the rule is wrong

A small design problem from our AI-assisted build process, and the working habit it produced.

The problem

I’m building a local-first productivity app with AI agents doing the coding. I’m the product manager, not a developer, so I don’t read the code. What lets me trust it is a set of written standards the agents build against, and automatic checks that hold the work to them.

In our process, Forge, we write standards (we call them component contracts) before we write code. Each one is a written rule about how part of the system must behave. Requirements and acceptance criteria are written against the standard, tests prove them, and a guard script (an automated check) fails the build if a change breaks a rule. The point is that the rule’s wording decides what passes.

Checked Off keeps its data in a local database on each device, so using it on several devices means syncing that data. Devices may run different app versions, so we need rules that stop a database change in one version from breaking sync with another. One of them says that when the app changes its database (a “migration”), the result for each row must be the same on every device. That keeps devices from drifting apart.

One table keeps a record of what the app has already shown a person. Each device uses its own ids and timestamps, because the record is about that device’s history. A separate step later merges any duplicates. But the standard said every device must produce identical rows, while the design said each device’s rows must contain its own unique data. The two could not both be true. The guard, which checks for identical rows, would reject a correct migration, and there was no truthful way to declare it compliant.

The usual fix, and a better one

We say “the exception proves the rule,” but “prove” there originally meant “test.” Our rule failed the test.

The usual fix is an exception: “all rows must match, except this migration.” It’s quick, and the guard passes. But an exception admits the rule was wrong without correcting it. And, in our experience, exceptions pile up.

We stopped and asked what the rule protects. The answer wasn’t identical rows. It was that devices must not disagree about shared data in a way sync can’t settle. Identical rows are one way to guarantee that. The records table is another, because the difference is defined by a written contract and reconciled by a named later step.

So we restated the rule around that aim. Rows that depend only on their own values agree by construction. Some rows record something true of one device only, and differ by nature. The rule now names those kinds in a short closed list (a fixed set, not open-ended). Each kind carries its own obligation: the contract that defines the difference and the step that reconciles it. That list is part of the rule, not an appendix of exceptions, so the guard can still check it.

Two checks kept us honest. An AI reviewer agreed with the analysis but rejected my first wording: “differs by nature” left the guard nothing to test. That pushed us to the closed list. And we searched for every place that quoted the old wording, seven in all, so the change landed everywhere at once.

Making it a habit

Our process has a working preferences document. It records how I want the work done and the hard-won lessons that keep tripping us. The orchestrating AI (the one that coordinates the other agents) reads it at the start of every session, and again whenever its working memory is condensed mid-session. Here is what we added, word for word:

Avoid Exceptions

When a rule, requirement, criterion or test blocks something that has to happen, do not reach for an exception first. Ask what the rule protects, and whether the blocked thing threatens that. If it does, change what you are doing.

If it does not, then the rule is worded for a means when it should state its aim: restate it so it guards what it must and does not block what it need not. Keep the restated rule checkable. Where the world has distinct kinds of cases, name them as a closed list, each with the obligation that keeps the aim true, so the list is part of the rule and not an appendix of exceptions. Before applying, say what the old wording rightly caught that the new one still catches.

A true one-off exception is rare; if you find yourself keeping a list of them, the rule needs restating. Tells: “except”, “unless”, “only when” in a draft; a declaration someone cannot truthfully make; a test or criterion that fails a legitimate case.

What this is really about

Most process improvement isn’t a grand redesign. It’s small and incremental. You find a good way to solve one problem. You work out how to generalize it. Then you systematize it in a process document or a working preference, so the next problem starts from the better habit instead of the old one. Over time those small additions are what make the process good.

None of this is specific to AI. Any organization with standards, whether a policy, a review checklist or a release gate, will see exceptions pile up when a standard is worded for a method instead of its goal. The fix is the same: ask what the standard protects, and say that. The exception doesn’t prove the rule. It tests it, and when it fails, fix the rule.