SaaS platform engineering
A pattern for building and running multi-tenant SaaS as a platform rather than a growing application. It covers tenancy and isolation, entitlements, billing, tenant-aware data access, background work and the delivery pipeline that lets a small team ship safely and often.
When this pattern is the right one.
Multi-tenancy is usually treated as a schema decision and then becomes a source of incidents across the whole codebase. Product teams also accumulate bespoke variants per customer, so the platform becomes a growing set of conditionals that nobody can reason about.
Tenant identity is spread across the codebase instead of being enforced at one boundary
Background jobs and caches have no reliable notion of which tenant they belong to
Plan limits and entitlements are checked in the interface but not in the domain logic
Every customer-specific request produces another branch and another regression risk
Releases are infrequent because the pipeline is slow, which makes each release larger and riskier
How the work is done.
We make tenancy a property of the request and of the data access layer, so a query cannot accidentally cross a customer boundary. Entitlements become a first-class domain concept evaluated server-side, which means the interface can never grant a capability the plan does not include. Feature differences are modelled as configuration and extension points rather than branching. Everything else follows from delivery discipline: fast pipeline, reversible releases, and per-tenant observability so a single customer problem can be diagnosed without affecting the rest.
Tenant context resolved once per request and enforced at the data-access boundary
Entitlements and plan limits evaluated in domain logic, never only in the interface
Configurable feature flags replacing per-customer code branches
Usage metering and billing events recorded immutably and reconciled against provider data
Tenant-scoped queues, caches, rate limits and observability dimensions
Trunk-based delivery with feature flags and automated rollback triggers
What this is usually built with.
Chosen per problem rather than run as one stack for everything. The list below is representative, not a commitment.
The outcome this pattern is chosen for.
This pattern is designed to support a product that can be sold to organisations of different sizes and shapes without the codebase fragmenting. It is built to keep the cost of adding a tenant low and the risk of leaking one low.
Keeps the cost of onboarding a new customer shape low and predictable
Makes isolation a property of the platform rather than a discipline every developer must maintain
Lets plan and pricing changes ship as configuration rather than as a release
Shortens the path from commit to production while keeping each release reversible
Makes a single tenant’s failure diagnosable without impacting the others
The parts that tend to go wrong.
Written down because they are the same parts that go wrong everywhere, and knowing them up front is cheaper than discovering them halfway through.
Retrofitting tenant scoping after launch means auditing every query, and the gaps are found in production
Schema-per-tenant approaches make migrations, backups and connection management dramatically harder
A metered usage pipeline that is not idempotent produces billing disputes that are expensive to unwind
Feature flags accumulate; without ownership and expiry dates they become a second, undocumented codebase
Premature multi-region design adds cost and complexity long before it is required
These are solution patterns, not client case studies. Each one describes a reusable engineering approach and the problem shape it addresses. No client is named and no result is claimed.
Nothing here reports a measured outcome. The outcome sections describe what each pattern is designed to support, and what a team should expect to be able to do once it is in place.
Named references, implementation detail and engagement history can be discussed directly on request, subject to client confidentiality.
This page is structured so that verified case studies can be added alongside these patterns without changing the underlying format.
Recognise your situation in this one?
Patterns are general. The engineering decisions are not, and they are worth discussing against your actual constraints.