Skip to content

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:

  1. Pause + copy: take tenant offline for minutes, copy data, point router at new cell.
  2. 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 k random cells out of N.
  • Two tenants overlap in O(k²/N) cells on average.
  • A single cell failure hits k/N of tenants.

Deployment waves#

  1. Wave A (1 cell) - canary
  2. Wait 30 min, watch SLOs
  3. Wave B (2 cells)
  4. ... 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.

Further reading#