Scaling a SaaS from first customer to hundredth
The architectural decisions that get painful between one and a hundred tenants, and which ones to make early.
The first customer forgives everything. The tenth notices inconsistency. The hundredth is why you should have thought about tenancy, limits and observability before you had ten.
Decide tenancy early, because migrating is brutal
Shared schema with a tenant column is simplest and scales furthest. Database-per-tenant gives isolation your enterprise buyers may demand and multiplies your operational work. Choose consciously — this is the decision that is genuinely expensive to reverse.
Configuration, not forks
First big customer
- Special-casing
- A branch, shipped quickly
- Configuration
- A flag, shipped quickly
Tenth
- Special-casing
- Ten branches to keep in sync
- Configuration
- Ten flag combinations, all tested
Upgrade path
- Special-casing
- Each customer migrated by hand
- Configuration
- One deploy
Ceiling
- Special-casing
- How many branches you can maintain
- Configuration
- Much higher
| Special-casing | Configuration | |
|---|---|---|
| First big customer | A branch, shipped quickly | A flag, shipped quickly |
| Tenth | Ten branches to keep in sync | Ten flag combinations, all tested |
| Upgrade path | Each customer migrated by hand | One deploy |
| Ceiling | How many branches you can maintain | Much higher |
Put the tenant id in everything
Every table, every log line, every metric, every background job. The first time you need to answer 'is this slow for everyone or just them', you will be glad. Retrofitting it across a live system is miserable.
Noisy neighbours arrive sooner than you expect
One customer bulk-importing will degrade everyone else unless you have limits. Per-tenant rate limits and queue fairness feel like premature optimisation right up to the afternoon they are not.
Configuration, not forks
The pressure to special-case a big customer is immense and it compounds. Push variation into configuration and feature flags. A codebase with per-customer branches has a hard ceiling on how many customers you can serve.
Roughly when each thing starts to hurt
- 1
Tenant id everywhere
Day one. Retrofitting it across a live system is miserable, and you will need it the first time one customer reports slowness.
- 2
Tenant-scoped logs and metrics
Around customer five, when "is this slow for everyone?" becomes unanswerable.
- 3
Per-tenant rate limits
The first time someone runs a bulk import and degrades everyone else.
- 4
Self-service onboarding
Before you become the bottleneck — usually earlier than planned.
- 5
Data export
When your first enterprise contract asks about exit.
What to build before a hundred
In rough order of when the pain arrives:
- Tenant-scoped logging and metrics
- Per-tenant rate limits and quotas
- Self-service onboarding, or you become the bottleneck
- A safe way to run one-off scripts against a single tenant
- Data export, for both sales and exit obligations