Software Engineering
What RLS-By-Default Actually Costs You
The rule is simple to state
Every table gets row-level security enabled, default-deny, explicit-allow. No table ships with RLS off "for now." That's a standing engineering standard, not a per-project judgment call -- which means the cost of it has to be worth paying every single time, not just when it's convenient.
The benefit is real
Security logic lives in the database, enforced on every access path -- a direct API call, a server action, an admin script -- not re-implemented per route and inevitably forgotten in one of them. A common pattern in this kind of system: a function like a private "is admin" check lives outside the schema that gets exposed as a public API, so it's usable inside a policy but can't be called directly as a public endpoint. The authorization check and the data access are the same operation, not two systems that have to agree with each other.
The cost shows up in three places
Performance. A naive RLS policy calls the current-user check once per row instead of once per query -- invisible at ten rows, very visible at ten thousand. The fix is small (wrapping the call in a subquery so Postgres evaluates it once) but you have to know to do it, and a query planner won't warn you in plain language that you forgot.
Bootstrapping. Default-deny means the very first write to a brand-new table has nowhere to go until a policy explicitly allows it -- which means every new table needs its policy written and tested before the feature using it can work at all, not after. That's friction by design, not an oversight.
Self-referential tables are genuinely tricky. A table that stores admin permissions has to have RLS policies that don't depend on a query against the very table they're protecting -- get this wrong and you can lock every admin out of their own permissions table with no way back in except direct database access. That's a real, specific failure mode, not a hypothetical one.
Is it worth it
For anything that handles real user data, yes -- consistently, not "yes for the important tables." The discipline of writing the policy before the feature is part of what makes default-deny worth the friction: it forces the access-control question to be answered explicitly, in one place, rather than scattered across application code where it's easy to get right in nine places and wrong in the tenth.
This is the same standard applied across our products and every client engagement under Services -- not a one-off choice for any single project.
