Most SaaS products that struggle to scale didn't get the architecture wrong because the team wasn't smart — they got it wrong because multi-tenancy was treated as something to "figure out later." By then, the data model is baked in and untangling it is a rewrite.
Decide your isolation model early
There are three common approaches:
- Shared schema, tenant_id column — simplest, cheapest, scales well with proper indexing.
- Schema-per-tenant — stronger isolation, more operational overhead.
- Database-per-tenant — maximum isolation, justified for enterprise/compliance needs.
For 90% of products, a shared schema with a disciplined tenant_id strategy and row-level security is the right call. We used exactly this on Ezanias TMS to serve multiple trucking companies from one codebase.
Make tenant scoping impossible to forget
The single biggest source of multi-tenant bugs is a query that forgets its tenant_id. Don't rely on developers remembering — enforce it:
alter table orders enable row level security;
create policy tenant_isolation on orders
using (tenant_id = current_setting('app.tenant_id')::uuid);
Index for the access pattern, not the table
Every tenant-scoped query starts with tenant_id. Your indexes should too — composite indexes led by tenant_id keep the 1,000th tenant as fast as the first.
The goal isn't to handle scale someday. It's to make scale a non-event.
If you're planning a SaaS and want the foundation right from day one, let's talk.