Skip to main content
KillaQ
Architecture7 min read

Multi-tenant from day one: the decisions you cannot undo

Data isolation, foreign keys and migrations: why bolting on multi-tenancy later costs ten times more.

Written by KillaQ

Some architecture decisions can be reversed in an afternoon. Others shape the product for years. In a SaaS, the tenancy model belongs firmly to the second group.

The mistake that sounds sensible

"Let's launch with one customer and add multi-tenancy when we need it." It sounds prudent, but it means every table is born without a tenant column, every query without a filter, every foreign key without an ownership constraint. When the second customer arrives, the migration is not adding a column — it is auditing every query in the system and proving that none of them crosses the boundary.

Isolation: three models

  • Shared rows with a discriminator. A tenantId column on every table. Simplest to operate, cheapest to scale. It demands absolute discipline: no query may ever run unfiltered.
  • Schema per tenant. Stronger isolation and independent backups, at the cost of migrations you have to apply N times.
  • Database per tenant. Maximum isolation. Only worth it with a small number of large customers, or where regulation requires it.

For most products, shared rows are the right answer — provided the filter does not depend on a developer remembering it.

Make the filter non-optional

The difference between a safe system and one that eventually leaks data between customers is where the filter lives. If it lives in each service, someone will eventually write the query that skips it.

Put it where it cannot be forgotten: in the data-access layer, in the request context, or in the database itself with row-level security. Then add a test that fails when a query appears without a tenant constraint.

Foreign keys have tenants too

An easily missed detail: if Invoice belongs to Customer and both carry a tenantId, the foreign key must guarantee that both belong to the same tenant. Without that composite constraint, a poorly validated id in a form lets a record from one tenant be attached to another.

What can wait

Not everything has to exist on day one. Per-tenant billing, plans, usage limits and the admin panel can all arrive later at no structural cost. Isolation cannot.

Back to blog

Related articles

Performance6 min read

Core Web Vitals in Next.js: what to measure and what to ignore

LCP, CLS and INP explained without the hype, plus the three causes behind most production failures.

Read article
Mobile5 min read

Offline-first is not a feature: it is an architecture

How to design an idempotent write queue so an app keeps working when the network drops.

Read article