|$ curl https://forge-ai.dev/api/markdown?path=docs/system-design/scalability
$cat docs/scalability.md
updated Recently·35 min read·published

Scalability

System DesignIntermediate🎯Free Tools
Introduction

Scalability is the ability of a system to handle growth — more users, more data, more traffic — without a proportional loss in performance or availability. It is not a single technique but a set of architectural decisions that compound over time.

There are two primary dimensions of scale. Scale up means making individual machines bigger and faster. Scale out means adding more machines and distributing work among them. Modern distributed systems overwhelmingly prefer scale out because commodity hardware is cheaper, fault tolerance is easier, and the ceiling is effectively unlimited.

This guide covers the building blocks of scalable systems: load balancing, stateless architectures, caching, database scaling, and auto-scaling. Each topic is accompanied by practical patterns and the tradeoffs they introduce.

Horizontal vs Vertical Scaling

Vertical scaling, or scaling up, increases the resources of a single node: more CPU cores, more RAM, faster disks, better network cards. It is simple because the architecture does not change, but it has hard physical and economic limits. The largest single machine is always smaller than the largest cluster.

Horizontal scaling, or scaling out, adds more nodes to a pool and distributes work across them. It introduces complexity — coordination, load balancing, data partitioning — but it also provides elasticity, redundancy, and the ability to use commodity hardware.

DimensionVertical ScalingHorizontal Scaling
Cost curveExponential for high-end hardwareRoughly linear per unit of capacity
Fault toleranceSingle point of failureResilient to individual node failures
ComplexityLow — no distributed-system concernsHigh — networking, consensus, partitioning
ElasticitySlow — hardware procurement or VM resizeFast — spin up containers or instances
CeilingLimited by largest available machineEffectively unbounded

best practice

Start with vertical scaling for prototypes and small products. Transition to horizontal scaling when you need redundancy, geographic distribution, or capacity beyond a single machine.
Load Balancing

A load balancer distributes incoming traffic across a pool of backend servers. It is the simplest way to add horizontal scale and fault tolerance to the application tier. Without it, a single popular server becomes a hotspot and a single point of failure.

load-balancer.txt
TEXT
1┌─────────────┐
2│ Client │
3└──────┬──────┘
4
5┌──────▼──────┐
6│ DNS / │
7│ CDN │
8└──────┬──────┘
9
10┌──────▼──────────────────────────────┐
11│ Load Balancer (L4/L7) │
12│ Round-Robin / Least-Connections / │
13│ IP Hash / Consistent Hash │
14└───┬───────┬───────┬───────┬─────────┘
15 │ │ │ │
16┌───▼───┐ ┌─▼─────┐ ┌─▼─────┐ ┌─▼─────┐
17│ App-1 │ │ App-2 │ │ App-3 │ │ App-4 │
18└───────┘ └───────┘ └───────┘ └───────┘

Layer 4 vs Layer 7

Layer 4 load balancers operate on transport-layer information such as IP address and TCP port. They are fast and stateless but cannot inspect HTTP headers or route based on URL paths. Layer 7 load balancers understand application protocols, enabling path-based routing, SSL termination, sticky sessions, and request rewriting.

AlgorithmHow It WorksBest For
Round-RobinCycles through backends in orderHomogeneous servers, equal load
Least-ConnectionsRoutes to the backend with fewest active connectionsVariable request durations
IP HashHashes client IP to pick a backendSession affinity without cookies
Consistent HashHashes a key and maps it to a ringCache servers, minimizing reshuffling
WeightedAssigns proportionally more traffic to powerful nodesHeterogeneous fleet

warning

Sticky sessions couple a user to a specific server. Prefer externalizing session state to Redis or cookies so that any healthy backend can serve any request. Sticky sessions hide state and make scaling and failover harder.
Stateless vs Stateful Services

A stateless service does not store client-specific data between requests. Any request can be handled by any instance, which makes horizontal scaling trivial. A stateful service retains information about the client or connection, such as a WebSocket session or an in-memory shopping cart.

Stateless does not mean the system has no state. It means the state lives outside the application server — in a database, cache, object store, or message queue. This separation is the single most important enabler of elastic scale.

stateless-flow.txt
TEXT
1Stateless request flow:
2
3Client ──▶ LB ──▶ App Server A
4 reads/writes state from Redis/DB
5 returns response
6
7Next request:
8Client ──▶ LB ──▶ App Server B
9 reads same state from Redis/DB
10 no loss of context
AspectStatelessStateful
ScalabilityTrivial — add nodes behind LBHard — must route users to right node
FailoverInstant — any node can take overComplex — state must be migrated or rebuilt
Use casesREST APIs, microservices, batch workersWebSockets, game servers, real-time collab

info

If you think you need stateful servers, first ask whether the state can be moved to Redis, a database, or a client-side token. Only keep state in memory when latency or throughput requirements make external storage impossible.
Caching for Scale

Caching is the fastest way to reduce load on databases and improve response times. A cache stores copies of frequently accessed data in fast memory, closer to the consumer. The cache hit rate determines the effectiveness of the strategy.

Caches exist at every layer of the stack: browser caches for static assets, CDN edge caches for content, application caches for hot objects, and database buffer pools for frequently read pages. The closer the cache is to the user, the lower the latency and the less load on origin systems.

cache-aside.txt
TEXT
1Read path with cache-aside:
2
31. App receives request for user profile
42. Check Redis cache for key user:1234
53. If cache HIT: return value immediately
64. If cache MISS:
7 a. Read from database
8 b. Write result to cache with TTL
9 c. Return result
10
11Write path:
121. Update database
132. Invalidate cache key (or update it)
143. Next read repopulates cache

best practice

Design for cache misses. A cache should accelerate the common case, not hide a design that collapses when the cache is empty. Always ensure the origin can handle a full cache miss at peak load.
Database Scaling

Databases are often the first bottleneck in a growing system. Scaling them involves a progression of techniques: vertical scaling, read replicas, caching, partitioning, sharding, and eventually specialized databases for different access patterns.

Read Replicas

Read replicas copy the primary database's data asynchronously. They offload read traffic and improve availability. The tradeoff is replication lag: a write on the primary may not be visible on replicas for milliseconds or seconds. Applications must tolerate stale reads or route critical reads to the primary.

Sharding

Sharding splits data across multiple database instances based on a shard key. Each shard holds a subset of the data, allowing the system to scale write throughput linearly. The challenge is choosing a shard key that distributes load evenly and minimizing cross-shard queries.

shard-key.txt
TEXT
1Shard key selection for a user database:
2
3Option 1: user_id % number_of_shards
4- Even distribution if user_id is random
5- Hot shard risk if a region or tenant dominates
6
7Option 2: Hash(user_id)
8- Better distribution than modulo
9- Resharding requires remapping all data
10
11Option 3: Range-based (user_id 1-1M → shard 1)
12- Easy range queries within a shard
13- Risk of hot ranges (new signups hit latest shard)
14
15Option 4: Composite (tenant_id + user_id)
16- Groups a tenant's data together
17- Can create tenant-level hot shards
TechniqueScalesComplexityTradeoff
Vertical scalingSingle nodeLowHard ceiling, single point of failure
Read replicasRead throughputLowReplication lag, no write scaling
CachingHot readsMediumStale data, invalidation complexity
PartitioningSingle DB, large tablesMediumQuery planner complexity
ShardingWrite throughputHighCross-shard queries, resharding pain

warning

Do not shard prematurely. The operational cost of sharding is high. Exhaust vertical scaling, caching, and read replicas first. Shard only when write throughput or data size exceeds what a single primary can handle.
Auto-Scaling

Auto-scaling adjusts the number of running instances based on demand. It prevents over-provisioning during quiet periods and under-provisioning during spikes. Cloud providers offer horizontal pod autoscalers, VM scale sets, and serverless functions that scale to zero.

autoscaling.txt
TEXT
1Common auto-scaling metrics:
2
3- CPU utilization → scale when compute-bound
4- Request latency (p95/p99) → scale when response times degrade
5- Request queue depth → scale when backlog grows
6- Custom metrics → business events, queue length, error rate
7
8Scaling policies:
9- Target tracking → keep metric near a setpoint
10- Step scaling → add/remove fixed capacity at thresholds
11- Scheduled scaling → pre-warm before known events
12- Predictive scaling → ML-based forecast of upcoming load

Auto-scaling is not instantaneous. It takes time to launch instances, pull container images, and pass health checks. If your traffic spikes faster than your scale-up time, you need a combination of caching, rate limiting, and over-provisioned headroom.

info

Set scale-down policies conservatively. Rapid scale-down can terminate instances that are still finishing requests. Use connection draining, graceful shutdown hooks, and cooldown periods to avoid dropping traffic.
Scalability Patterns

Beyond the basics, several architectural patterns help systems scale under specific constraints. Knowing when to apply them separates mid-level from senior engineers.

CQRS

Command Query Responsibility Segregation separates read and write models. Writes go through a strict, normalized model; reads are served from denormalized, query-optimized views. This pattern shines when read and write patterns differ dramatically.

Event Sourcing

Instead of storing only the current state, event sourcing stores a log of immutable events. State is derived by replaying events. It provides a complete audit trail and enables temporal queries, but it adds complexity to schema evolution and snapshotting.

Bulkheads

Bulkheads isolate failures by partitioning resources. If one customer or feature exhausts a pool, the rest of the system continues to function. This is a reliability pattern that also improves scalability under stress.

Rate Limiting

Rate limiting prevents any single client from overwhelming the system. It protects downstream services and ensures fair resource allocation. Token bucket and sliding window are the most common algorithms.

🔥

pro tip

The most scalable systems are not those with the most nodes — they are those that do the least work per request. Push computation to the edge, cache aggressively, and batch writes wherever possible.
$Blueprint — Engineering Documentation·Section ID: SD-SCL-01·Revision: 1.0