There's a moment in every SaaS build where you have to answer one uncomfortable question: is a tenant a row, a schema, or a database? The answer determines your blast radius, your migration story, and how expensive your smallest customer is.
The Three Common Models
- Shared schema with tenant_id column — cheapest, hardest to secure
- Schema-per-tenant — clean isolation, painful migrations at scale
- Database-per-tenant — strongest isolation, highest infra cost
We chose shared-schema with strict Row-Level Security. Every query carries an implicit tenant filter enforced by the database, not the application. It's the only model that keeps a bug in one code path from leaking across customers.
The Non-Obvious Costs
RLS is not free. Complex policies with joins can turn a 5ms query into 300ms without warning. We invest heavily in policy auditing, EXPLAIN dashboards, and a rule that no policy references more than one other table.
"Multi-tenancy is not a feature you add. It's a constraint you design around from the first migration.
Operational Lessons
- Backfill scripts must be tenant-scoped and resumable
- Rate limits belong at the tenant level, not the user level
- Every log line should carry a tenant_id — you will need it at 3am
- Feature flags are your safety valve for rolling out per-tenant behavior
Tags
Written by
Omar Rahal
Principal Engineer
Passionate about technology and its potential to transform businesses. Leading innovation at AiFlAi.


