Every tenant's data lives in the same table, separated by a column. Every query filters on tenant_id. Until one doesn't.
A developer writes a reporting query at 11pm, forgets the filter, and one customer sees another customer's records. No attacker, no exploit, no clever payload — a clause that wasn't typed.
Isolation is a spectrum. A shared table is cheapest and densest, and its safety rests entirely on discipline. A schema per tenant makes the mistake harder to make by accident. A database per tenant gives a clean blast radius and makes every migration run N times. A cluster per tenant buys total isolation at the highest operating cost.
Underneath all four sits a better question: can the filter be forgotten at all? Row-level security moves it into the database, where the policy applies to every query whether or not the developer remembered to write it.
Good design doesn't ask people to be careful. It removes the opportunity to be careless.
