DSA Guide
System Design

System Design

How to design a system that handles real traffic — the building blocks, the trade-offs, and a repeatable method for working through an unfamiliar problem

Algorithm problems have one right answer. System design does not.

Ask ten engineers to design a photo-sharing service and you will get ten different diagrams, several of them good. What separates a strong design from a weak one is not the boxes — it is whether the person can explain why each box is there, what it costs, and what breaks when traffic grows ten times.

That is what this section teaches.

What You Are Actually Being Asked

A design question is deliberately vague. "Design a URL shortener" tells you almost nothing: how many users, how fast, read-heavy or write-heavy, does it need analytics?

The vagueness is the point

A well-specified problem tests whether you can code. A vague one tests whether you can find the important questions before answering.

The first five minutes should be spent narrowing the problem, not drawing. A design built on unstated assumptions is a design nobody can evaluate — including you.

The Method

Every problem in this section is worked with the same six steps. Follow them in order and an unfamiliar problem becomes a familiar procedure.

Clarify and scope

Ask what the system must do and, just as importantly, what it need not do. Write down the two or three features you will actually build.

"Shorten a URL and redirect. No custom aliases, no analytics, no expiry. Agreed?"

Put numbers on it

Estimate reads per second, writes per second, and storage growth per year. You are not aiming for accuracy — you are aiming for the order of magnitude, because that is what decides the architecture.

100 writes per second and 100,000 writes per second are different systems.

Define the interface

Name the handful of operations the system exposes. Two or three endpoints is usually enough.

This is where you notice a design is doing too much.

Sketch the simplest thing that works

One service, one database. No cache, no queue, no sharding.

Starting simple is not naive — it gives you a baseline to improve, and it shows you understand that complexity must be earned.

Find the bottleneck, then fix that one thing

Look at your numbers and ask what breaks first. Add exactly one component to fix it. Then ask again.

Each addition should answer a specific pressure you have already named.

Say what you traded away

Every choice costs something. A cache adds staleness. A queue adds delay. Sharding makes joins hard.

Naming the cost is the single clearest signal that you understand your own design.

The most common mistake

Drawing the final architecture immediately — load balancer, cache, sharded database, message queue, CDN — before any numbers exist.

It looks impressive and it is hollow. If you cannot say what pressure each component relieves, the design is memorised, not reasoned. Someone will ask "why the queue?" and there will be no answer.

Start simple. Add under pressure. Always.

From Simple to Scaled

Here is the shape almost every design follows. The point is not that the final picture is correct — it is that each step is caused by the previous step's limit.

One system, growing under pressure
HTTPClientLoad BalancerApp Serversstateless ×NCacheRedisDatabaseprimaryRead ReplicasQueueWorkers
ClientEdgeServiceCacheDatabaseQueue
stage1. Simplest thing that works
One server, one database. This genuinely handles a few hundred requests per second. Start here every time.
1 / 5
Five steps, five named pressures. Never add a box without one.

What This Section Covers

Foundations — the building blocks

Each explains one component and, more importantly, what it costs.

Numbers Every Design Needs

Latency figures, capacity estimation, and turning 'a million users' into requests per second

Networking Basics

DNS, TCP vs UDP, the HTTP versions, TLS, and what a CDN actually does

Scaling

Vertical vs horizontal, why stateless matters, and load balancing

Caching

Where to put a cache, eviction, invalidation, and the three failure modes

Databases

SQL vs NoSQL, indexing, replication, and sharding

Storage Engines

Object vs block vs file, and the B-tree / LSM-tree split

Partitioning and Consistent Hashing

Splitting data without reshuffling it, plus geohashing for location

Consistency and CAP

What you give up when data lives in more than one place

Distributed Primitives

Leader election, consensus, distributed locks, and logical clocks

Queues and Async Work

Decoupling, back pressure, delivery guarantees, and idempotency

APIs and Rate Limiting

Endpoint design, pagination, idempotency keys, and protecting the system

Services and Boundaries

Monolith vs services, where to draw lines, and the machinery it costs

Search Systems

The inverted index, tokenisation, ranking, and index freshness

Probabilistic Structures

Bloom filters, HyperLogLog and count-min sketch — accuracy traded for memory

Security

Authentication vs authorisation, tokens, encryption, and the attacks to design against

Reliability

Failure is normal — timeouts, retries, circuit breakers, degradation

Observability

Logs, metrics and traces, and defining what 'working' means

Deployment and Migrations

Canary releases, feature flags, and changing a schema without downtime

Worked Designs

Each applies the six steps end to end, and ends with what it traded away.

How to Read This Section

Read the foundations first, then one worked design

The foundation pages each explain one building block and, more importantly, what it costs. The worked designs then assemble those blocks under pressure.

Reading a worked design without the foundations gives you a diagram to memorise. Reading the foundations first gives you the ability to derive the diagram — which is the actual skill.

There is no code in this section. System design is about trade-offs between components, not implementations, and a page full of code would hide that.

  • Design — the same combine-structures thinking, at the scale of a single machine
  • Heaps — the structure behind rate limiters and priority queues
  • Hash Maps — consistent hashing, used for sharding, is built on this idea

On this page