Early on, I designed for the happy path and hoped the rest would sort itself out. When something broke, my instinct was to keep the system “working” — to let requests through so users wouldn’t hit a wall. It felt user-friendly. It was quietly dangerous.
The moment that changed my mind was small. An auth check timed out — the service behind it was briefly down — and the code, trying to be helpful, treated “couldn’t verify” as “must be fine” and let the request through. Nothing bad happened that day. But I sat there realizing I’d built a system that opened its doors precisely when it was least sure who was knocking.
That’s backwards, and now I design the opposite way. When something fails — the permission service, the token check, the thing that decides who’s allowed — the default has to be no. Deny, don’t grant. Fail closed, not open. The inconvenience of a false “no” during an outage is nothing next to the cost of an accidental “yes.”
Convenience always argues for failing open. It’s the easier user experience, the fewer support tickets, the smoother demo. Security means being willing to choose the less convenient default on purpose, before the outage chooses for you.
So I decide it up front now, deliberately, for anything that gates access: when in doubt, the system says no. A door that unlocks itself whenever the guard faints was never really locked.
– Serguey Asael Shinder
Leave a Reply