Common system design patterns laid out as building blocks

Common Patterns

The recurring system design interview patterns — realtime updates, long-running tasks, contention, scaling reads and writes, large blobs, multi-step workflows, and proximity — with the trade-offs and failure modes behind each.

Why patterns win interviews

Combine the technologies and concepts from earlier posts and you can build almost anything. But under interview time pressure, what wins is recognizing patterns. Spot the pattern a design needs, and you fall back on a known playbook instead of reinventing it live.

Analogy diagram: masala recipe cards for realtime, async, contention, and combos.
Recognize the pattern, pull the matching playbook — stop reinventing it from scratch.
01Name it

Say the pattern in the first 5 minutes.

02Start simple

Polling before WebSockets; one DB before shards.

03Fail early

Call out classic failure modes yourself.

04Combine

Real systems are coalitions of patterns.

Recognizing patterns is often what separates senior engineers from junior ones. A pattern tells you what's interesting and what's not, and surfaces failure modes before you hit them. Interviewers aren't grading how many boxes you draw — they're grading whether you name the shape of the problem early and spend the clock on the hard edges.

Interview takeaway

A pattern is a named package of decisions — components, failure modes, and deep-dive topics. Name it, then build.

Pattern map

These patterns aren't mutually exclusive — most designs use several at once. Skim this map first, then jump to the section you need.

RTRealtime

Chat, scores, live dashboards.

LRLong tasks

Encode, reports, bulk jobs.

CTContention

Tickets, auctions, inventory.

SRScale reads

Feeds, catalogs, hot content.

SWScale writes

Ingest, telemetry, social graph.

LBLarge blobs

Video, images, documents.

MSMulti-step

Orders, onboarding, payments.

PXProximity

Uber, delivery, local search.

Practice drill

After every mock: which patterns did I name? Which did I miss? Which trade-offs did I skip?

Pattern detection checklist

Before you draw, listen for requirement keywords and map them to a pattern. This is the 60-second skill that separates "I know Redis" from "I know what problem Redis solves here."

HearLive / chat / scores

→ Realtime updates

HearEncode / PDF / export

→ Long-running tasks

HearLast seat / auction

→ Contention

HearFeed / catalog / viral

→ Scale reads

HearIngest / telemetry / UPI

→ Scale writes

HearVideo / image upload

→ Large blobs

HearOrder / pay / ship

→ Multi-step

HearNearby / drivers

→ Proximity

  • "Users need updates as they happen" → Realtime (start polling; escalate to SSE/WebSockets).
  • "Takes more than a few seconds" → Long-running tasks (job ID + workers).
  • "Two users fighting for one resource" → Contention (conditional update before distributed locks).
  • "Everyone is reading the same hot content" → Scale reads (index → replicas → cache → CDN).
  • "Writes are crushing one DB" → Scale writes (shard key first; queues for bursts).
  • "Gigabyte files through the API" → Large blobs (presigned URL; app is control plane only).
  • "Payment then inventory then ship" → Multi-step / saga (name compensations).
  • "Find nearest X within Y km" → Proximity (city/region first, then geo index).

Why this checklist matters: interviewers often bury the pattern inside a product story. "Design Instagram Stories" is really large blobs + long-running transcode + scale reads + maybe realtime for views. If you only hear "Instagram," you will invent random microservices. If you hear the checklist triggers, you land on a coalition in under a minute — and the interviewer knows you have judgment.

Interview takeaway

When you hear X, name pattern Y out loud — then build the simplest version of Y before stacking more patterns.

Pushing realtime updates

In many systems, you'll need to make updates to the user in real time. For synchronous APIs, this is as simple as returning a response once the request is completed. For other systems like chat applications, notifications, or live dashboards, you'll need to be able to push updates to the user as they happen.

There are a lot of decisions to make when implementing realtime updates. First, choose a protocol. Simple HTTP polling is simplest; SSE and WebSockets are purpose-built but need infra care. Full deep dive: Real-time updates (two hops, L4/L7, pub/sub vs hash ownership). Also see Networking essentials.

Updates flow to Server then Client, with two challenges: how updates propagate to clients, and how the server gets triggered.
Realtime Updates Challenges

Notice the diagram frames two separate problems. Interviewers love when you split them explicitly:

  1. Step 1 — Client delivery: How do updates propagate to clients? (polling / SSE / WebSockets / push notifications)
  2. Step 2 — Server trigger: How does our server get notified when updates happen? (pub/sub, DB change streams, webhooks, polling a source of truth)

Protocol comparison (say this in interviews):

  • HTTP polling — client asks every N seconds. Easy with load balancers and CDNs. Wasteful under high churn; latency = poll interval.
  • Long polling — server holds the request until an event (or timeout). Better latency than short polling; still HTTP-friendly.
  • SSE — unidirectional server → client over HTTP. Great for live scores, stock tickers, progress bars. Auto-reconnect is built into browsers.
  • WebSockets — full duplex. Required for chat typing indicators, multiplayer cursors, collaborative editing. Harder: sticky sessions, connection fan-out, reconnect storms.

For the server side of realtime updates, you again have more options. Pub/Sub services are a common way to decouple the publisher and subscriber (used in WhatsApp-style chat breakdowns), while stateful servers in a consistent hash ring (or similar) can be used when processing is heavier (Google Docs–style collaboration, where each document has an owning server).

Why interviewers push here: the protocol choice is the easy half. The hard half is fan-out and reconnect. If one chat message must reach 1M online clients, a single app process cannot hold 1M sockets and also do business logic. You put a connection gateway tier in front, fan out via pub/sub, and accept that some clients will miss events during a blip — so you need a cursor or last-event-id to catch up. Trade-off: sticky sessions simplify routing but make rolling deploys and autoscaling painful; gateways + pub/sub scale better but add another hop and failure mode. Expect the next probe to be: "What happens when the client reconnects after 30 seconds offline?"

Deep-dive topics interviewers expect:

  • How do you fan out one message to 1M online clients? (pub/sub fan-out, connection gateways)
  • What happens on disconnect / reconnect? (missed events, last-event-id, cursor)
  • Sticky sessions vs connection gateways for WebSockets
  • Backpressure when clients are slow
  • Online presence and heartbeats
Interview takeaway

Split realtime into two questions — how clients get updates, and how the server gets triggered — then start with polling.

Managing long-running tasks

Many operations in distributed systems take too long for synchronous processing — video encoding, report generation, bulk operations, or any task that takes more than a few seconds. The Managing Long-Running Tasks pattern splits these operations into immediate acknowledgment and background processing.

When users submit heavy tasks, your web server instantly validates the request, pushes a job to a queue (like Redis or Kafka), and returns a job ID within milliseconds. Separate worker processes continuously pull jobs from the queue and execute the actual work. This provides fast user response times, independent scaling of web servers and workers, and fault isolation.

Client talks to Server over HTTP; Server adds job to DB and job queue; Workers pull jobs and update status.
Long Running Tasks

Walk the diagram left to right in an interview:

  1. Client → Server over HTTP (validate + ack fast)
  2. Server persists the job in the database (status = queued)
  3. Server enqueues work on the Job Queue
  4. Workers pull jobs, do the work, update job status in the DB
  5. Client polls GET /jobs/:id (or gets a push) for progress / result

Why the queue is not free: you now own job state, retries, DLQs, visibility timeouts, and "what if the worker dies mid-job." Sync keeps back-pressure natural — if the server is overloaded, clients wait or get 503. Async hides overload until the queue length explodes and users see "still processing" forever. Interviewers probe next on idempotency (same job twice), poison messages (always fail), and whether status lives in the queue (bad) or a durable DB (good). Trade-off in one line: queues buy UX and scale isolation; they cost operational surface area.

The key technologies are message queues for job coordination and worker pools for processing. You'll need to handle:

  • Job status tracking — queued → running → succeeded / failed (store in DB, not only in the queue)
  • Retries — with backoff; decide max attempts
  • Dead letter queues (DLQ) — for poison messages that keep failing
  • Idempotency — workers may see the same job twice (at-least-once delivery)
  • Visibility timeout / lease — so a crashed worker doesn't leave the job stuck forever
  • Priority / rate limits — VIP reports shouldn't starve behind bulk exports

When NOT to use this pattern: login, payment authorization confirmation the user is staring at, anything under ~1–2 seconds where UX is worse with a spinner + poll loop. Sync is simpler and often correct.

Related reading: Message queues for delivery semantics (at-least-once vs exactly-once). Full deep dive: Managing long-running tasks.

Interview takeaway

Ack fast with a job ID; process in workers — but only queue when the work is truly too slow for the HTTP request.

Dealing with contention

When multiple users try to access the same resource simultaneously — like booking the last concert ticket or bidding on an auction item — you need mechanisms to prevent race conditions and ensure data consistency. This pattern addresses coordination challenges in distributed systems.

Alice and Bob interleave seat decrements and payments, then both read one seat available.
Example of a race condition

In the diagram, Alice and Bob both believe they got the last seat because their read–modify–write steps interleaved. Walk this failure mode before proposing a fix — it shows you understand the bug, not just the buzzwords.

Solution ladder (start at the bottom):

  1. Single-row DB transactionUPDATE seats SET count = count - 1 WHERE id = ? AND count > 0 (atomic check + decrement)
  2. Pessimistic lockingSELECT … FOR UPDATE; blocks other writers until commit
  3. Optimistic concurrency — version column; fail and retry if someone else wrote first
  4. Queue-based serialization — put all bookings for a show into one queue so workers process sequentially
  5. Distributed locks — Redis / ZooKeeper / etcd when the resource spans services
  6. Two-phase commit / sagas — when multiple systems must agree (payment + inventory)

Trade-offs include performance versus consistency, and simple database solutions versus complex distributed coordination. Most problems start with single-database solutions before scaling to distributed approaches.

Why "just use Redis lock" is a junior reflex: a distributed lock does not magically fix payment + inventory spanning two services, and holding a lock across a 30-second payment call creates timeouts and deadlocks under load. Prefer a short reservation (seat held for 2 minutes with a TTL) plus a conditional commit when payment succeeds. Interviewers probe next: payment succeeds but seat update fails; replica lag shows stale "available"; and whether eventual consistency is acceptable for inventory displays (often yes) vs checkout (usually no). Name what you are giving up before you split data across systems — that is the senior signal.

Deep-dive prompts to prepare for:

  • What if the payment succeeds but the seat update fails?
  • How long do you hold a lock during payment? (short hold + reservation TTL is safer)
  • How do you prevent oversell under replica lag?
  • When is eventual consistency acceptable for inventory?

Full deep dive: Dealing with contention (conditional writes, FOR UPDATE, OCC, write skew, distributed leases, deadlocks & ABA). Related: CAP theorem · Consistency models.

Interview takeaway

Start with a single-DB conditional update — reach for distributed locks only after you can name what you're giving up.

Scaling reads

Spotting the pattern is what separates a senior answer from a scramble.

As your application grows from hundreds to millions of users, read traffic often becomes the first bottleneck. While writes create data, reads consume it — and read traffic typically grows much faster than write traffic. The Scaling Reads pattern addresses high-volume read requests through database optimization, horizontal scaling, and intelligent caching.

For most applications, the read-to-write ratio starts at 10:1 but often reaches 100:1 or higher. Consider Instagram: when you open the app, you see dozens of photos requiring hundreds of database queries for metadata, user info, and engagement data. Meanwhile, you might only post once per day — a single write operation.

Client to Server; Server checks cache first, then database on cache miss.
Database Read Scaling

The diagram shows the classic cache-aside read path: (1) check cache first, (2) check DB on cache miss. In an interview, expand that into the full progression:

  1. Optimize inside one database — proper indexes, covering indexes, avoid N+1 queries, denormalize hot paths
  2. Read replicas — send reads to replicas; writes stay on primary. Watch replication lag.
  3. Application cache (Redis) — cache-aside or read-through for hot keys
  4. CDN / edge cache — for public, mostly-static responses and media

Key considerations (these are the deep dives):

  • Cache invalidation — TTL vs explicit delete on write; stampede protection (singleflight / lock)
  • Replication lag — user writes then immediately reads stale data; read-your-writes via primary for that session
  • Hot keys — millions requesting the same viral post; local cache + CDN + key sharding
  • Cache hit ratio — measure it; a 50% hit ratio may not justify the complexity

Why this order exists: each step buys you a different kind of relief and costs a different kind of correctness. Indexes are almost free correctness-wise. Replicas introduce lag — the classic "I just posted and don't see it" bug — so you need read-your-writes for that session. Cache introduces invalidation and stampede risk. CDN is farthest from truth and best for public, slowly changing bytes. Interviewers probe next on hot keys (one viral post), stampede after TTL expiry, and whether you invalidate on write or only rely on TTL. If you cannot explain invalidation, you are not ready to draw Redis.

Full deep dive: Scaling reads (indexes → replicas → Redis → CDN, hot keys, stampede, versioning). Also: Caching strategies · Database indexing.

Interview takeaway

Index → replicas → Redis → CDN. Don't jump to Redis before proving the query plan and hit-rate assumptions.

Scaling writes

As your application grows from hundreds to millions of writes per second, individual database servers and storage systems hit hard limits. The Scaling Writes pattern addresses write bottlenecks through sharding, batching, and intelligent load management.

The core strategies are horizontal sharding (distributing data across multiple servers), vertical partitioning (separating different types of data), and handling write bursts through queues and load shedding. Key considerations include selecting good partition keys that distribute load evenly while keeping related data together.

  • Horizontal sharding — split rows by key across servers (users A–M on shard 1, N–Z on shard 2)
  • Vertical partitioning — split by table/column type (profile DB vs activity DB)
  • Write queues — buffer temporary spikes so the DB isn't crushed
  • Load shedding — drop or defer low-priority writes under overload
  • Batching — group many small writes into fewer round trips

For burst handling, you can use write queues to buffer temporary spikes or implement load shedding to prioritize important writes during overload. Batching techniques help reduce per-operation overhead by grouping multiple writes together.

Writes through gateway to queue buffer and shards.
Database Write Scaling
Good key hash UserID keeps partitions under capacity; bad key Country creates a China hotspot.
Partitioning by State

The diagram is the interview punchline. Good key: hash(UserID) spreads writes evenly under server capacity. Bad key: Country puts China on one shard that blows past capacity while New Zealand idles. Same idea applies to sharding on created_at day — today's shard melts during a festival sale.

How to choose a shard key (checklist):

  • High cardinality (many distinct values)
  • Even access distribution (no celebrity hotspot unless mitigated)
  • Keeps related data together for your common queries (avoid scatter-gather)
  • Stable — don't pick a key that changes often (re-sharding is painful)

Cross-shard pain you should name: secondary indexes, unique constraints, transactions across shards, rebalancing when a shard grows. Interviewers respect candidates who admit these costs before proposing 64 shards.

Why sharding early is a trap: you trade one hard problem (one busy primary) for many harder ones (routing, rebalancing, cross-shard joins, dual writes during migration). Queues and batching often buy months of runway without that tax. Interviewers probe next on celebrity hotspots (one user_id owns too much write traffic), how you reshard online, and whether your common query becomes scatter-gather across all shards. If the answer to "what's your shard key?" is vague, pause — the key choice is the design.

Full deep dive: Scaling writes (vertical + DB choice → shard/partition → queues & shedding → batching / hierarchy). Also: Sharding · Scalability · Kafka.

Interview takeaway

Pick a high-cardinality even key (hash UserID), name hotspots, and admit cross-shard pain before proposing 64 shards.

Handling large blobs

Large files like videos, images, and documents need special handling in distributed systems. Instead of routing gigabytes through your application servers, this pattern uses direct client-to-storage transfers with presigned URLs and CDN delivery.

Client requests a pre-signed URL via API Gateway and Server, then transfers 2GB directly to Blob Storage.
Large Blobs

Your application server generates temporary, scoped credentials (presigned URLs) that let clients upload directly to blob storage like S3. Downloads come from CDNs with signed URLs for access control. This eliminates your servers as bottlenecks while providing resumable uploads, progress tracking, and global distribution.

Walk the diagram in two phases:

  1. Control plane: Client → API Gateway → Server requests a pre-signed URL. Server creates it with local credentials (scoped, time-limited).
  2. Data plane: Client PUTs the 2GB once straight to Blob Storage — bypassing app servers entirely.

Key challenges include:

  • State sync — DB metadata (s3_key, size, status) vs actual object in storage
  • Upload failures — multipart / resumable uploads; orphaned partial objects
  • Lifecycle — delete, expire, archive to cold storage
  • Access control — short-lived signed URLs; never make the bucket public by default
  • Virus scanning / processing — often a long-running task after upload completes

Why direct-to-storage wins: pushing a 2GB wedding video through your app servers burns CPU, memory, and connection slots that should serve API traffic. The trade-off is consistency between "object exists in S3" and "row exists in DB" — clients can abort mid-upload, leaving orphans, or succeed in S3 while your DB write fails. Solve with multipart uploads, lifecycle rules for incomplete parts, and an event (ObjectCreated) that drives your state machine. Interviewers probe next on access control (never public buckets by default), resumable uploads on flaky mobile networks, and the coalition with long-running tasks for transcoding.

Interview combo: Large Blobs often pairs with Managing long-running tasks (transcode after upload) and Real-time updates (progress %). Name that coalition early. Full deep dive: Handling large blobs.

Interview takeaway

Control plane mints presigned URLs; data plane ships gigabytes straight to blob storage — never through your app servers.

Multi-step processes

Complex business processes often involve multiple services and long-running operations that must survive failures, retries, and external dependencies. This pattern provides reliable coordination for workflows like order fulfillment, user onboarding, or payment processing.

API Server writes orders to Event Store; Payment, Inventory, Shipping, and Email workers consume topics; Payment Service via webhook.
Multi-Step Processes

Solutions range from simple single-server orchestration to sophisticated workflow engines and durable execution systems. Event sourcing provides a distributed approach where each step emits events that trigger subsequent steps. Modern workflow systems like Temporal or AWS Step Functions handle state management, failure recovery, and retry logic automatically.

The diagram shows an event-driven choreography style:

  1. Client places an Order with the API Server
  2. API Server writes the order into an Event Store (topics: Order, Payment, Inventory, Shipping)
  3. Specialized workers react: Payment, Inventory, Shipping, Email
  4. Payment Workers talk to an external Payment Service and get results via Webhook

The key insight is moving from scattered state management and manual error handling to declarative workflow definitions where the system guarantees execution semantics and maintains complete audit trails.

  • Orchestration — one conductor tells each step what to do (Step Functions, Temporal, a workflow service)
  • Choreography — services react to events (looser coupling, harder to see the full story)
  • Sagas — each step has a compensating action on failure (refund if restaurant rejects)
  • Idempotency keys — retries must not double-charge

Why scattered if/else fails at scale: each service has partial knowledge, failures leave the system in undefined states, and nobody can answer "where is order 42 stuck?" Orchestration costs a central workflow dependency but gives you visibility and retries in one place. Choreography scales teams independently but makes the full story hard to debug — interviewers love asking you to narrate a failure mid-flow. Trade-off: start with a state machine table in one service; graduate to Temporal/Step Functions when you need durable timers, human approvals, or multi-day waits. Next probe: "Payment charged, restaurant rejected — walk me through compensation."

Interview takeaway

Prefer a clear state machine (or Temporal) over scattered if/else — and always name the compensating action on failure.

Proximity-based services

Several systems like Design Uber or Design Gopuff will require you to search for entities by location. Geospatial indexes are the key to efficiently querying and retrieving entities based on geographical proximity.

User query filtered by city or region first, then geohash radius returns nearest drivers.
Proximity — region filter first, then geo index

These services often rely on extensions to commodity databases like PostgreSQL with PostGIS, Redis geospatial data types, or dedicated solutions like Elasticsearch with geo-queries enabled. Deep dive on trees vs encoded keys: Proximity search.

The architecture typically involves dividing the geographical area into manageable regions and indexing entities within these regions. This allows the system to quickly exclude vast areas that don't contain relevant entities, thereby reducing the search space significantly.

  • Geohash / S2 / H3 cells — encode lat/long into buckets; query neighboring cells
  • Quadtrees / R-trees — classic spatial index structures (PostGIS uses R-trees)
  • Redis GEO — fast radius queries for online drivers / couriers
  • Two-level filter — first by city / region shard, then radius within the region

Why region-first matters: a global geo index sounds impressive and is almost always the wrong default. Most queries are local, and location writes from drivers are high-churn — you need a write path that can absorb frequent pings without melting a single store. Trade-off: coarser cells mean more false positives you filter in memory; finer cells mean more neighbor lookups at cell boundaries. Interviewers probe next on ping frequency vs battery, how you expire stale drivers, matching latency under surge, and whether you need a purpose-built index at all for ~1,000 entities (usually no — scan is fine).

Note that most systems won't require users to be querying globally. Often, when proximity is involved, it means users are looking for entities local to them.

Interview deep dives: how often do drivers ping location? How do you avoid stale positions? How do you pick the nearest N available drivers without sorting the entire city every time?

Interview takeaway

Shard by city first; geo-query locally. Skip purpose-built geo indexes until you have hundreds of thousands of entities.

Pattern selection

These patterns often work together to solve complex system design challenges. A video platform might use Large Blobs for video uploads, Long-Running Tasks for transcoding, Realtime Updates for progress notifications, and Multi-Step Processes to coordinate the entire workflow.

More coalitions you'll see constantly:

TicketsBookMyShow

Contention + reads + multi-step pay.

ChatWhatsApp-style

Realtime + write ingest + history reads.

FeedNews feed

Scale reads + fan-out jobs + cache.

RidesUber / Ola

Proximity + realtime + matching.

The key is recognizing which patterns apply and understanding their trade-offs. Start simple (polling, one DB, single orchestrator) and only add complexity when requirements demand it.

Practice drill

Before drawing boxes, write three lines: (1) patterns involved, (2) simplest starting design, (3) first failure mode you'll deep-dive.

Next: apply these on URL shortener, News feed, and Rate limiter — and name the patterns as you use them.

Cost and performance levers

Pattern composition studio

Pattern ↔ classic question map

QuestionPatterns to name
URL shortenerCache-aside, ID generation
News feedFan-out, cache timeline
ChatPub/sub, presence, fan-out
Upload videoBlob store, async jobs
PaymentsIdempotency, saga

Failure modes to mention

Call out at least one dependency failure (DB down, cache stampede, queue lag, region outage) and your mitigation (timeouts, retries with jitter, degraded mode, circuit breaker).

Interview Q&A by level

Practice saying these out loud for common system design patterns. Interviewers grade clarity and judgment more than buzzwords.

Interview takeaway

Match depth to the bar: define → trade off → operate. Don't dump principal answers in an entry-level screen.

← Lattice