Lesson 1 of 5
What Is System Design?
"Simplicity is prerequisite for reliability." — Edsger W. Dijkstra
For four Volumes you have made single things fast and correct: a sort, a query, a React component, a clever O(n) trick that beats the O(n²) one. That was engineering in the small — a function you can hold in your head, with one right answer waiting to be found. System design is engineering in the large. It asks a different question entirely: not "is this function correct?" but "will this whole thing stay up, stay fast, and stay affordable when a hundred million people lean on it at once — and can you defend every choice you made to get there?" There is no single right answer here. That is not a flaw of the discipline; it is the discipline. This is the layer interviewers use to separate people who can code from people who can build systems — and it is where senior careers are made.
You can write the function. But how do you design the whole system around it — the servers, caches, databases, and queues — so it survives real scale, real failure, and a real budget? And why is there no single "correct" design?
Core Concepts
System design is the practice of deciding the architecture, components, and data flow of a software system so that it meets its functional requirements and its non-functional goals — scale, reliability, performance, and cost — under real-world constraints. In Volume 4 you designed an algorithm: a self-contained recipe that turns an input into an output on one machine. In system design you assemble many moving parts — machines, networks, storage, background jobs — into something that behaves like one coherent service. The unit of work jumps from "a function" to "a service," and everything about how you reason has to jump with it.
The defining shift is this: in DSA there is usually a best answer; in system design there is only the best answer for these constraints. Ask "SQL or NoSQL?" and the honest reply is always "it depends — on your read/write mix, your consistency needs, your team, your budget." Every real decision buys you something and costs you something else. Your job is not to find the mythical optimal design; it is to make defensible trade-offs and to say out loud what you traded and why. An interviewer who hears "I'd use a cache" learns little. One who hears "I'd add a cache because reads outnumber writes 100:1, accepting that cached data can be a few seconds stale" is watching an engineer think.
The building blocks you'll assemble
Almost every system you will ever design — and every one in this Volume — is built from the same small kit of parts. Here they are, and the whole rest of Volume 5 is a deep dive into each:
- Client — the browser, mobile app, or another service that makes requests.
- Load balancer (LB) — spreads incoming requests across many app servers so no single one is overwhelmed, and routes around dead ones.
- App servers — the stateless machines that run your business logic. Stateless is the magic word: any server can handle any request, so you scale by adding more.
- Cache — a fast in-memory store (Redis, Memcached) that keeps hot data close, so you don't hit the slow database for the same answer twice.
- Database — the durable source of truth. Relational (MySQL, Postgres) or non-relational, but always the thing that must not lose data.
- Message queue — (Kafka, RabbitMQ, SQS) decouples work: hand a task off now, let a worker finish it later.
- Worker — a background process that drains the queue and does slow work (sending email, encoding video) off the request path.
- CDN — a globally distributed cache for static assets (images, JS, video), serving them from a city near the user.
Here is how the core of them fit together in a small-but-real web system — the canonical shape you will draw a hundred times:
Read the arrows as the life of a request. The client never talks to an app server directly — the load balancer stands in front so you can add or lose servers without the client noticing. An app server checks the cache first; on a hit it returns in under a millisecond, on a miss it reads the database and usually writes the answer back to the cache for next time. Anything slow — sending a welcome email, resizing an upload — gets pushed onto the queue and handled by a worker later, so the user's request returns now. That single diagram already encodes half a dozen trade-offs, and learning to see them is the point of this Volume.
Scale changes everything — numbers drive design
The most important idea in system design is that the right architecture depends on the scale you're designing for, and scale is a number. A system for 100 users and a system for 100 million users are not the same design made bigger — they are different designs. At 100 users, a single server with a single database, hosted for a few dollars a month, is not only adequate, it is correct: it is simple, cheap, and easy to reason about. At 100 million users that same design is impossible — one machine cannot hold the data, serve the traffic, or survive its own failure.
Between those extremes, every jump in scale forces a new decision. This is why senior interviews open with "how many users? how many requests per second? how much data?" — because the numbers, not your preferences, dictate the shape. We'll make this rigorous in the estimation chapter; for now, absorb the instinct: a design is only right or wrong relative to a scale, and you cannot design without first asking for the numbers.
Designing a system is like designing a place to serve food. For your family, a home kitchen is perfect — one cook, one stove, no coordination. For a 50-seat restaurant you need a professional kitchen, waiters, a booking system. To feed a city you need a fleet of trucks, warehouses, and franchises. Nobody builds a distribution network to feed their family, and nobody feeds a city from a home kitchen. The same meal demands wildly different systems at different scales — and choosing the wrong one for your scale is the classic engineering mistake in both directions.
Functional vs non-functional requirements
Every design starts by separating two kinds of requirements, and mixing them up is a rookie tell.
- Functional requirements are what the system does — the features. "A user can post a tweet." "A user can shorten a URL and be redirected." These map to your API and your data model.
- Non-functional requirements are how well it must do them — the qualities. "Redirects must return in under 100 ms." "The service must stay up 99.9% of the time." "It must handle 10,000 writes per second." "Data must never be lost."
Juniors design for the functional requirements and stop. Seniors know the non-functional ones — scale, latency, availability, durability, cost — are what make system design hard, because those are the goals in tension with each other. Scoping a problem into these two buckets is so central it gets its own chapter; here, just plant the vocabulary.
Here is the trade-off that sits underneath almost every early architectural decision — the fork between staying simple and going distributed:
| Dimension | Simple (monolith + one DB, single host) | Distributed (many services, replicated/sharded, multi-host) |
|---|---|---|
| Complexity | Low — one codebase, one deploy, easy to reason about | High — network calls, partial failure, coordination, observability needed |
| Cost | Cheap — a single small server | Expensive — many machines, more ops, a team to run it |
| Scale ceiling | Limited by one machine's RAM/CPU/disk | Effectively unbounded — add more machines |
| Failure blast radius | The whole app goes down together | Isolated — one service can fail while others serve |
| Development speed (early) | Fast — no distributed-systems tax | Slow — you pay coordination costs before you need to |
| Right for | Startups, internal tools, most products at first | Proven products at real scale with clear bottlenecks |
What experienced designers actually do in the first five minutes of a design:
• They ask for numbers before drawing anything. "How many daily active users? Read-heavy or write-heavy? How big is a record?" The numbers pick the architecture.
• They separate functional from non-functional out loud. "Features are X, Y, Z; the hard part is 10k writes/sec at under 200 ms — that's what I'll design around."
• They start with the simplest thing that works and add complexity only where a number forces it. Every box on the diagram must earn its place by removing a specific bottleneck.
• They name every trade-off. "This buys us throughput at the cost of consistency" is the sentence that gets you hired.
System design became a named discipline because a handful of companies hit walls nobody had hit before. When Google needed to index the whole web, Amazon to survive Black Friday, and Netflix to stream to millions of homes, the single big server — "scaling up" by buying a beefier machine — simply ran out of road. Their answer, in the 2000s, was to scale out: many cheap, unreliable machines coordinated in software, designed on the assumption that individual machines will fail. Google's papers on GFS, MapReduce, and Bigtable, and Amazon's Dynamo paper, essentially wrote the field's foundations in public. Everything in this Volume — load balancing, replication, sharding, eventual consistency — descends from that shift from one big machine to many small ones.
This very site is a working lesson in right-sizing. chika.ng runs as a single Next.js app talking to one MySQL database, on one modest host, for a few dollars a month. No load balancer, no cache tier, no message queue, no microservices. Is that "wrong"? Absolutely not — it is correct for its scale. The traffic fits comfortably on one machine, the data fits in one database, and the simplicity means one person can understand, deploy, and debug the whole thing. Bolting on Kafka and a sharded cluster "to be ready for scale" would add cost, latency, and failure modes to solve a problem the site does not have. The skill this Volume teaches is not "always build the big architecture." It is knowing exactly which box to add, and when the numbers finally justify it.
Common Mistakes
- Over-engineering for scale you don't have. Reaching for microservices, Kafka, and sharding on day one, for a product with a thousand users. You pay all the complexity cost and get none of the benefit. Simple first; scale when a number forces it.
- Jumping to a design before clarifying requirements. Drawing boxes before you've asked "how many users? read- or write-heavy? what's the latency target?" The numbers are the design brief — start there.
- Ignoring the actual bottleneck. Optimizing the app servers when the database is the thing falling over. Find the real constraint before you add machinery; the fastest fix is to the part that's actually slow.
- Designing single points of failure. One load balancer, one database, no replica — a single box whose death takes the whole system down. Reliability means asking "what happens when this dies?" of every component.
- Hand-waving the numbers. "It'll be fast" and "it can scale" are not answers. Back-of-the-envelope estimation — QPS, storage, bandwidth — is the difference between an opinion and a design.
- Cargo-culting big-company architecture. "Netflix uses microservices, so we will too." Netflix has thousands of engineers and web-scale traffic; you have neither. Copy the reasoning, not the diagram.
You build a URL shortener. It's an instant hit: overnight it goes from 1,000 links a day to 1 billion redirects a day. Nothing about the feature changed — shorten a URL, redirect on click. But list what breaks. Can one database hold billions of rows and serve tens of thousands of redirects per second? Can one server survive the traffic — or its own reboot? Where would a cache help, given that a handful of viral links get most of the clicks? Notice you cannot answer any of this without numbers, and notice that the "correct" design flipped completely while the requirements stayed identical. That is why scale is the first question, not the last.
Interview Corner
- Sketch the canonical small-web-system diagram from memory — client, load balancer, two app servers, cache, database, queue, worker — and label the path of a single read request and a single "send welcome email" request.
- Take a product you use daily (a chat app, a photo feed, a ride-hailing app). Write down three functional requirements and three non-functional ones, and mark which non-functional one you think is hardest to hit.
- Say out loud, in one breath, the trade-off of going from one database to many: what it buys you and what it costs you.
- For chika.ng's single-server design, name the one number (users, requests/sec, or data size) that, if it grew 1,000×, would force the first new box onto the diagram — and say which box you'd add.
Chapter Summary
- System design is architecting the components and data flow of a whole system to meet functional requirements and non-functional goals — scale, reliability, performance, cost — the layer above writing a single algorithm.
- There is no single correct answer: the discipline is making, and justifying, trade-offs for the given constraints. Every choice buys one property at the cost of another.
- The building blocks are a small kit: client, load balancer, app servers, cache, database, message queue, worker, CDN — the rest of the Volume deep-dives each.
- Scale changes everything: a design for 100 users and one for 100 million are different designs, not the same one enlarged. Numbers drive the architecture.
- Functional requirements are what the system does; non-functional requirements (latency, availability, durability, cost) are how well — and they are what make design hard.
- The core early trade-off is simple monolith vs distributed: simplicity, low cost, and one deploy versus unbounded scale and failure isolation. Start simple; add complexity only where a measured number forces it.
- Right-sizing is a skill in both directions — over-engineering for scale you don't have is as much a mistake as under-building for scale you do.
Now you know what the discipline is — architecture, data flow, and above all trade-offs driven by numbers. But most of you will first meet system design not on the job, but in a 45-minute room (or an AI screen) where someone says "design Twitter" and starts a timer. That conversation has its own rules, rhythm, and traps — and knowing them is half the battle. Next, in The System Design Interview, we turn the mindset of this chapter into a repeatable playbook for the room itself.
Editorial reflection — the hardest thing to unlearn coming out of algorithms is the search for the right answer. There isn't one here, and chasing it will paralyse you. System design rewards a different habit of mind: state your assumptions, pick a reasonable path, name what you traded to walk it, and stay ready to change course when a number moves. That is not a lesser kind of correctness — it is what engineering actually looks like once the problems get big enough to matter. Learn to be comfortable defending a choice rather than finding a truth, and you will have learned the real subject.
Q&A · 0
Enrol to ask questions and join the discussion.
No questions yet — be the first to ask.