Choosing a tenancy model before it chooses you
There are three practical isolation models for a SaaS product, and the right one is decided by what your customers require and what your team can operate — not by which is technically purest. Whichever you choose, enforce it in the data-access layer, because isolation maintained by developer discipline eventually fails.
Most SaaS products begin as one application for one customer. The second customer is accommodated with a configuration flag, the fifth with a column, and somewhere around the tenth the shortcuts become the roadmap. By then the tenancy question has reached into the data model, every query path, the cache, background jobs, file storage, exports and the audit trail — all at once.
The three models, and what each actually costs
The pattern in that table is consistent: isolation is bought with operational cost. Stronger separation means more databases to migrate, monitor, back up, patch and pay for. A product with two thousand small tenants cannot economically run two thousand databases; a product with eleven banks as customers probably must.
Questions that actually decide it
- Will customers contractually require physical separation or data residency in a specific region? If yes, that usually settles it before any technical argument.
- How often must a single tenant's data be exported or restored to a point in time, independently of everyone else?
- What is the expected tenant count and the distribution of their sizes? Many small tenants favour sharing; few large ones make isolation affordable.
- What operational capacity does your team actually have? Per-tenant databases multiply every routine task.
- What is the blast radius you can tolerate — how many tenants may a bad migration affect?
Enforce isolation where it cannot be forgotten
If you choose a shared schema, the critical engineering decision is not the column; it is where the filter is applied. Isolation that depends on every developer remembering a WHERE clause on every query has a defect rate above zero, and the defect is a cross-tenant data leak.
- Apply row-level security in the database so the engine enforces scoping even when application code is wrong
- Carry tenant context in a request-scoped ambient context rather than passing it by hand through each call
- Apply a global filter in the data-access layer so the default query is scoped and bypassing it is explicit and reviewable
- Write a test that attempts a cross-tenant read and asserts it fails — run it in CI on every change
- Log and alert on any query executed without tenant context in production
Everywhere else tenancy leaks
Teams typically scope the primary database correctly and then lose isolation somewhere less obvious. The list is worth auditing explicitly.
- Cache keys that omit the tenant, serving one tenant's data to another
- Background jobs that resolve tenant context from the wrong place, or lose it entirely on retry
- File and object storage paths without tenant prefixes, plus signed URLs with over-broad scope
- Search indexes shared without a tenant filter applied at query time
- Exports, reports and webhooks that aggregate across tenants
- Logs and error reports that include another tenant's payload in a shared trace
One tenant should not be able to degrade another
Shared infrastructure means shared failure modes. A tenant importing two million rows, or one running an unbounded report, can degrade everyone. The controls are ordinary but must be deliberate: per-tenant rate limits, queue fairness so one tenant's backlog cannot monopolise workers, query timeouts and result caps, and connection-pool limits per tenant. Pagination defaults matter more than they appear to — an unbounded list endpoint is the usual culprit.
Retrofitting tenancy into an application that never had it
This is a common and survivable project, provided it is sequenced rather than attempted as one rewrite.
- Inventory every place tenant identity must reach: tables, queries, caches, jobs, files, search, exports, logs and audit records.
- Introduce the tenant column and backfill it, with the existing customer as tenant one. Nothing changes behaviourally yet.
- Move scoping into the data-access layer and add database-level enforcement. Ship this before any new tenant arrives.
- Add the cross-tenant access test to CI, and only then onboard a second tenant.
- Build provisioning so a tenant can be created without engineering involvement.
- Add metering at the source of each billable event, then billing on top of it.
Migrations are the recurring tax
A shared schema means one migration for everyone: fast, but a bad migration affects every customer simultaneously. Per-tenant schemas or databases mean orchestrating N migrations with partial-failure handling and a mixed-version period your application must tolerate. Either way, expand-and-contract is the discipline that keeps deployments safe: add the new structure, write to both, backfill, switch reads, then remove the old structure in a later release — never in the same one.
The decision is difficult to reverse and shapes cost, compliance posture and operational load for years. Record it as an architecture decision with the alternatives considered and the reasoning written down, so the team inheriting it understands why — and knows which assumptions would justify revisiting it.
Questions
Is a shared schema secure enough for regulated customers?
It can be, when isolation is enforced at the database level, encryption and key management are in place, access is audited, and you can evidence all of it. The obstacle is frequently procurement rather than engineering: some customers mandate physical separation as policy. Establish that requirement during the sales process, because it is an architectural constraint rather than a preference.
How do we give one customer a point-in-time restore on a shared schema?
By restoring a snapshot into an isolated environment, extracting that tenant's rows, and importing them back through an application-level path that respects referential integrity. It is workable but genuinely involved, which is why frequent per-tenant restore requirements are a strong argument for schema or database separation.
When should tenancy be introduced?
Before the second tenant, if the intent is to build a product. Enforcement at the data-access layer is comparatively cheap to add while there is one customer and grows more expensive with every tenant added before it exists.