Multi-tenancy is a data model decision
Every SaaS platform adopts a tenant isolation model from the start. Choosing it deliberately takes a week of design; changing it at scale can require a rebuild.
21 July 2026 · 8 min read

"We will add multi-tenancy later" is one of the more expensive sentences in software. Not because it is impossible — it is done regularly — but because it is almost never the change it sounds like. It sounds like adding a column. It is closer to changing what every query in the system means.
The reason is that a single-tenant application has an implicit assumption threaded through all of it: that every row in the database belongs to the same customer. Nothing states that assumption, which is exactly why it is hard to remove. It lives in the queries that do not filter, the caches that are not keyed, the background jobs that process "all" of something, and the admin screens that show everything because everything is one customer’s.
The three models, and what each one actually costs
There are three broad choices, and the interesting thing is that the trade-off is not really technical. It is about who your customers are and what they are going to ask you for in year two.

Shared schema, tenant column
Every table carries a tenant identifier and every query filters on it. One database, one schema, one migration to run.
This is the cheapest to operate and by far the cheapest to evolve — a schema change is a single migration rather than one per customer. It is what most successful SaaS products run on, and it is our default.
Its weakness is that isolation is enforced by correctness rather than by structure. One query missing its filter is a cross-tenant data leak, and that is the category of bug that ends companies. The mitigation is not discipline — discipline fails at 3am — it is making the unsafe query impossible to write. Postgres row-level security enforces the predicate in the database, underneath the application, so a forgotten WHERE clause returns nothing rather than returning someone else’s data.

Schema per tenant
One database, a separate schema per customer. Isolation is structural: a query in the wrong schema does not find the wrong data, it finds no table.
The cost arrives at migration time and it grows linearly with your success. Every schema change runs once per tenant, which is fine at 20 and an operational project at 2,000 — and partially-applied migrations across a subset of tenants is a genuinely unpleasant state to be in. Connection pooling gets harder too.
It earns its place when customers are few, large, and contractually entitled to something you can point at in an audit.
Database per tenant
Complete separation, including the ability to host different customers in different regions, restore one customer from backup without touching another, and hand a customer their own data as a database rather than an export.
It is the most expensive by a wide margin — in infrastructure, in deployment complexity, and in the amount of tooling you have to build before you can answer "how many active users do we have" across the whole estate. Choose it when a regulator or a contract requires it, not because it feels safest.
The parts nobody costs in advance
Whichever model you pick, these are the pieces that get discovered late, and they are the reason retrofitting is a rewrite rather than a refactor:
- Caching. Every cache key needs the tenant in it. A single un-keyed cache entry serves one customer’s data to another, intermittently, in a way that is very hard to reproduce.
- Background jobs. A job carries no request context, so the tenant has to be part of the job payload and re-established when it runs.
- File storage. Uploads need tenant-scoped paths and tenant-scoped signed URLs, or object keys become guessable across customers.
- Search indexes. External search engines do not know about your row-level security. The tenant filter has to be re-implemented there, correctly, separately.
- Anything that aggregates. Reporting, usage metering, admin dashboards — all of it now has two modes, per-tenant and cross-tenant, and the cross-tenant one has to be impossible to reach from a tenant-scoped session.
- Per-tenant configuration. Feature flags, branding, limits. These arrive in month three of every SaaS product without exception.
What we actually recommend
Shared schema with row-level security, unless something specific rules it out. It is the cheapest to run and the cheapest to change, and the enforcement can be made structural rather than relying on every developer remembering every time.
The condition attached is that the isolation has to be tested as a security property, not assumed as a design one. That means an automated test that authenticates as tenant A and attempts to read tenant B — across the API, the search index, the file store and the background jobs. If nothing in your test suite tries to breach isolation, you do not know that you have any.
And if you genuinely cannot decide yet: put the tenant identifier on every table from the first migration anyway. It costs almost nothing today, it makes the shared-schema path available for free, and it removes the single hardest part of moving to one later.
New writing, by email
Receive new articles by email. Unsubscribe at any time.
Related work
Custom SaaS Development
Multi-tenant platforms with billing, administration and access control built in.
Related questions
More writing
How we work · 6 min
Product · 7 min
Have a project in mind?
Start a project