Cell-based Architecture#
Problem statement (interviewer prompt)
A multi-tenant SaaS at scale must contain the blast radius of any single failure (bug, hot tenant, bad config push) to a small fraction of users. Design an architectural pattern that achieves this without sacrificing throughput.
A cell is a complete, self-contained stack (services, databases, caches) that serves a partition of tenants. Cells are identical, deployed independently, and isolated from each other. A failure in cell N affects only cell N's tenants.
flowchart TB
R[Cell router] --> C1[Cell 1<br/>tenants A-F]
R --> C2[Cell 2<br/>tenants G-M]
R --> C3[Cell 3<br/>tenants N-S]
R --> C4[Cell 4<br/>tenants T-Z]
subgraph C1[Cell 1]
A1[API] --> D1[(DB)]
A1 --> K1[(Cache)]
end
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
class R edge;
class A1 service;
class D1 datastore;
class K1 cache;
Pioneered by Amazon and Salesforce ("pods"). Used to bound blast radius for deploys, capacity, hot tenants, and even region-level failures.
Cell-based architecture (also called pods, shards, or stamps) is the cleanest answer to "how do I keep one failure from taking down everyone?" Each cell is a complete, identical stack serving a partition of users.
What lives in a cell#
flowchart TB
subgraph Cell[Cell]
LB[Cell-local LB] --> S1[Service A]
LB --> S2[Service B]
S1 --> D1[(DB shard)]
S2 --> Q[(Queue)]
S1 --> R[(Redis)]
end
CR[Global router] --> LB
Reg[Cell registry] -.tenant→cell.-> CR
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
class CR,LB edge;
class S1,S2 service;
class D1 datastore;
class R cache;
class Q queue;
A cell contains everything a tenant needs: API tier, workers, primary store, cache, queues, even monitoring. The only shared layer is a thin global router and a tenant→cell registry.
Sizing a cell#
- Small enough that a worst-case failure is acceptable. Typical: 5-10% of users.
- Large enough to amortize fixed cost (database license, control plane).
- Headroom of 30-50% so a tenant spike doesn't tip the cell.
Doubling cells halves blast radius and doubles operational surface area.
Routing#
| Strategy | When |
|---|---|
| Static (tenant → cell, stored in registry) | Predictable, easy migration |
Hash-based (hash(tenantId) % N) |
Simple, hard to rebalance |
| Consistent-hash ring | Minimal rehash on cell add/remove |
| Geo-affinity | Latency-sensitive tenants |
Always include a thin front door that does only one thing: route requests to the right cell. Front door logic must be near-zero so it never becomes the SPOF.
Shuffle sharding#
To further reduce blast radius, place each tenant in a random subset of cells. Two random tenants on average overlap in only one cell.
flowchart LR
T1([Tenant 1]) --> C1[Cell 1]
T1 --> C3[Cell 3]
T2([Tenant 2]) --> C2[Cell 2]
T2 --> C4[Cell 4]
T3([Tenant 3]) --> C1
T3 --> C2
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
For N cells and k shards per tenant, a single tenant's "fault domain" is C(N,k) distinct shards, so a single cell failure affects only k/N tenants. AWS Route 53 uses this for DNS.
Deployment waves#
Deploy to cells in waves. A bad config bakes in cell 1 for 30 minutes before reaching cell 2. Failures detected → halt.
sequenceDiagram
participant Dep as Deploy pipeline
participant C1 as Cell 1 (wave A)
participant C2 as Cell 2 (wave B)
participant Mon as Monitors
Dep->>C1: deploy v2
Mon-->>Dep: green for 30min
Dep->>C2: deploy v2
Mon-->>Dep: error budget burning
Dep-->>C2: rollback v2
Migrations between cells#
The hardest operation. Two approaches:
- Pause + copy: take tenant offline for minutes, copy data, point router at new cell.
- Dual-write + cutover: dual-write to old and new cells, backfill diffs, cutover atomically.
CDC and event sourcing (see CDC) make option 2 less painful.
Trade-offs#
| Pros | Cons |
|---|---|
| Bounded blast radius | More clusters to operate |
| Independent deploy cadence | Cross-cell joins are hard |
| Easier capacity planning | Cell migrations are expensive |
| Geo-affinity friendly | Cell-local hot spots persist |
Where cell-based fits#
flowchart TB
CB((Cell-based<br/>architecture))
DBS[Database sharding<br/>within-cell partitioning]
MR[Multi-region DR<br/>cells per region]
MT[Multi-tenancy<br/>silo / bridge implemented as cells]
DS[Deployment strategies<br/>wave deploys across cells]
CB --> DBS
CB --> MR
CB --> MT
DS --> CB
classDef client fill:#dbeafe,stroke:#1e40af,stroke-width:1px,color:#0f172a;
classDef edge fill:#cffafe,stroke:#0e7490,stroke-width:1px,color:#0f172a;
classDef service fill:#fef3c7,stroke:#92400e,stroke-width:1px,color:#0f172a;
classDef datastore fill:#fee2e2,stroke:#991b1b,stroke-width:1px,color:#0f172a;
classDef cache fill:#fed7aa,stroke:#9a3412,stroke-width:1px,color:#0f172a;
classDef queue fill:#ede9fe,stroke:#5b21b6,stroke-width:1px,color:#0f172a;
classDef compute fill:#d1fae5,stroke:#065f46,stroke-width:1px,color:#0f172a;
classDef storage fill:#e5e7eb,stroke:#374151,stroke-width:1px,color:#0f172a;
classDef external fill:#fce7f3,stroke:#9d174d,stroke-width:1px,color:#0f172a;
classDef obs fill:#f3e8ff,stroke:#6b21a8,stroke-width:1px,color:#0f172a;
class CB service;
class DBS,MR,MT,DS datastore;
Glossary & fundamentals#
| Tag | Concept | What it is | Page |
|---|---|---|---|
HLD |
Database sharding | within-cell partitioning | database-sharding |
HLD |
Multi region DR | cell granularity across regions | multi-region-dr |
HLD |
Multi-tenancy patterns | silo vs pool vs bridge models | multi-tenancy |
HLD |
Deployment strategies | wave-based rollouts across cells | deployment-strategies |
Quick reference#
Definition#
Identical, isolated stacks each serving a partition of tenants. A failure stays inside its cell.
Sizing#
- 5-10% of users per cell typical
- 30-50% headroom
- Cell count = blast-radius target / users-per-cell
Routing#
| Strategy | Use |
|---|---|
| Static registry | Predictable, easy migrate |
| Hash %N | Simple, painful rebalance |
| Consistent hash | Smooth add/remove |
| Geo-affinity | Latency tenants |
Shuffle sharding#
- Place each tenant in
krandom cells out of N. - Two tenants overlap in O(k²/N) cells on average.
- A single cell failure hits
k/Nof tenants.
Deployment waves#
- Wave A (1 cell) - canary
- Wait 30 min, watch SLOs
- Wave B (2 cells)
- ... Wave N (rest)
Migration#
- Pause + copy (outage)
- Dual-write + CDC cutover (online)
Used by#
AWS Route 53, Salesforce pods, Slack job queue, Slack teams, Atlassian shards, Cloudflare cells.
Trade-offs#
-
- Bounded blast radius
-
- Independent deploy
-
- More clusters to operate
-
- Cross-cell joins hard
Refs#
- AWS - Shuffle sharding builders library
- Slack - Scaling job queue
- Brooker - Cells: containing failures
FAQ#
What is cell-based architecture?#
Cell-based architecture partitions the system into many identical self-contained stacks called cells, each serving a slice of tenants. A failure in one cell only affects that slice, bounding blast radius.
How is cell-based architecture different from microservices?#
Microservices split by domain; cells split by tenant or workload partition. You can run cell-based architecture on top of microservices, replicating the whole service mesh inside each cell.
What is a cell router?#
A cell router is a thin stateless layer that inspects the tenant ID or partition key on each request and routes it to the correct cell. It must be tiny so it scales independently and stays highly available.
Why do AWS and Slack use cell-based architecture?#
Both run multi-tenant platforms where one bad deploy or a noisy tenant could break millions of users. Cells limit blast radius so deploys and failures only affect a small percent of customers.
What is shuffle sharding?#
Shuffle sharding assigns each tenant a random small subset of cells. Two tenants almost never share their full set, so a single tenant's bad traffic cannot take down everyone.
Related Topics#
- Database Sharding: cells often correspond to a sharded slice of all data
- Multi-tenancy Patterns: cells implement the silo and bridge multi-tenant models
- Multi Region DR: cells per region are a natural unit for disaster recovery