The previous piece argued that the shape of a permission model is a product decision. This one is about the less glamorous half: where the rule actually gets enforced. It’s easy to conflate the two, because a well-modeled system with checks in the wrong place looks correct from the outside — the interface shows each person exactly what they should see, everyone’s happy, and the permissions are effectively decorative.

Hiding a button is a usability improvement, not a control. The request it would have sent can still be constructed, and anything that isn’t the interface — the mobile client, the public API, a script someone wrote — never saw the button in the first place. That’s the familiar version. The subtler version is a check that lives in the request handler: real enforcement, but only on the paths that remembered to do it. The bulk export endpoint added later, the admin tool that queries directly, the background job that processes records on someone’s behalf — each is a place where the same data is reachable and the check is a separate piece of code that someone had to write again.

That’s the structural problem: scattered checks are checks you have to be perfect about. Every new endpoint is a new opportunity to forget, and forgetting produces no error, no failing test, and no visible symptom — just data quietly reachable by someone who shouldn’t reach it, discovered eventually by a customer or an auditor. Compare that to a check enforced where the data is fetched, so every path inherits it: the scope becomes part of asking for the records rather than a thing you remember to do after asking. The details differ by stack, but the property worth aiming for is that access is applied by construction rather than by discipline.

That property also gives you something to test. “Can this role reach this resource” is a question a test can ask directly and exhaustively, one role at a time, without needing to enumerate every route someone might use. It’s a small suite that catches an entire category of mistakes, and it stays useful precisely because it doesn’t depend on knowing which endpoints exist — which is what makes it survive the additions nobody told you about.

The related discipline is deciding what a denial should look like, since that’s also a design choice with consequences. Sometimes the honest answer is a clear “you don’t have access to this,” which helps the user ask the right person for it. Sometimes revealing the resource exists is itself a leak, and the correct response is indistinguishable from a missing record. Both are defensible; picking without thinking is how a system tells strangers which customer accounts exist. It’s the same pattern the whole thread keeps hitting — the model is the visible half, and the enforcement is where the model becomes true or doesn’t.