Skip to content

Architecture

Designing multi-tenant SaaS backends that scale

Claire Bennett

Most SaaS products start life with a single customer in mind. The first version works, the second customer signs, and a tenant_id column quietly appears on every table. That is a perfectly reasonable beginning. The trouble starts later, when the hundredth tenant arrives and every shortcut taken along the way sends its invoice at once.

Multi-tenancy is not one decision. It is a handful of them, and each can be made deliberately on day one for very little cost.

Pick an isolation model on purpose

There are three common ways to keep one customer's data away from another's:

  • Shared schema. Every tenant lives in the same tables, separated by a tenant key. Cheapest to run and simplest to migrate, but one missing filter is a data leak.
  • Schema per tenant. Each tenant gets its own schema inside a shared database. Stronger isolation, heavier migrations.
  • Database per tenant. Maximum isolation and the easiest story for enterprise buyers, at the highest operational cost.

Most products should begin with a shared schema and enforce isolation in the database itself, using row-level security rather than trusting every query to remember its WHERE clause. The goal is to make the safe path the default path.

If isolation depends on every engineer remembering a filter, it is not isolation. It is a habit.

Make tenancy a first-class concept

Tenant context should be resolved once, at the edge of the system, and carried everywhere after that: into queries, background jobs, caches, logs and metrics. When a queue worker picks up a job, it should know which tenant it is working for without anyone passing an extra argument by hand.

This pays off in three places:

  1. Debugging. Every log line and trace can be filtered by tenant.
  2. Fairness. Rate limits and job concurrency can be applied per tenant, so one noisy customer cannot starve the rest.
  3. Billing. Usage is already attributed, because attribution was never optional.

Design for the noisy neighbour

Sooner or later one tenant will be ten times larger than the others. Plan for it before it happens. Keep per-tenant quotas on expensive operations, partition large tables by tenant where the access pattern justifies it, and make sure a single tenant can be moved to dedicated infrastructure without a rewrite.

That last point matters more than it sounds. The ability to promote one customer to their own database is what lets a shared-schema product say yes to its first enterprise deal.

Keep the escape hatches open

Good multi-tenant architecture is mostly about preserving options. Abstract tenant resolution behind one interface. Keep migrations tenant-aware from the first one you write. Store configuration per tenant rather than in environment variables.

None of this is glamorous work, and none of it shows up in a demo. It is simply the difference between a platform that scales with its customers and one that has to stop and rebuild just as growth arrives.

More articles
All articles