← Back to notes

    constraint, not rule

    2026-04-20

    There's a permissions pattern I keep coming back to.

    You have data that's sometimes public and sometimes private. The default model is one table, a visibility column, a filter on every query. That works until somebody forgets the filter. Then the public list contains a private row. Once.

    The alternative is two different shapes. Public rows have one field NULL. Private rows have that field set. The public endpoint queries WHERE that field IS NULL. The private endpoint queries WHERE that field equals something. Neither endpoint can return the other's rows, not because of a check, but because of a WHERE clause that's structurally incapable. The bug class is gone, not mitigated.

    A rule is a thing somebody enforces. A constraint is a thing that's true by construction. Rules can be forgotten. Constraints survive everything that happens after you wrote them.

    This isn't a security trick, it's how good engineering works in general. Types over runtime checks, the compiler enforces. Migrations over "remember to" docs, the database enforces. Single source of truth over "keep these in sync," impossible to drift. One endpoint per use case over one endpoint with a flag, the URL enforces. In each case you're not asking the next person to remember something, you're making it impossible for them to do the wrong thing.

    This matters more now than it did three years ago because the next contributor on most codebases is no longer a human. It's an agent generating diffs from context. Agents are excellent at writing code that satisfies tests and matches existing patterns. They are terrible at noticing that a check exists somewhere far away that this new code needs to call. If correctness depends on humans remembering a rule, you've already lost. The agent forgets the rule the first time it has to reason across three files.

    Codebases that survive the next two years are the ones where the shape of the data and the shape of the API make the wrong thing harder to write than the right thing.

    There's a cost. Constraints are more annoying to design than rules. They force decisions earlier. You can't add visibility later if visibility is a structural property, you have to know on day one. That cost is the feature, not the bug. The decision you defer is the bug you ship.

    — Simon