Why application filtering alone is fragile
Application code has to remember. Every new query, every new report, every new background job is another opportunity to omit the tenant condition, and the omission does not look like an error — it looks like a page that works and shows more than it should. Row Level Security removes the dependence on memory: the condition is a property of the table, not of the query.
The four-part shape of a correctly secured table
- Create the table with an explicit tenancy column.
- Grant the specific privileges the intended roles need, and nothing more.
- Enable row-level security, which by itself denies everything.
- Write one policy per operation the application legitimately performs.
Coverage is the thing that actually rots
Policies are written when a table is created and then forgotten. A new operation is added later, a policy is dropped during a migration, or an operation is covered for one role but not another. The gap is invisible until someone hits it, which is why coverage deserves to be checked mechanically rather than reviewed occasionally.