Foundations of System Design

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.

The question this chapter answers

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:

Rendering diagram…

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.

Everyday analogy

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
The engineer's notebook

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.

History changed here

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.

Real-world example — chika.ng Academy

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.
Thought experiment

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

Interview Corner — the same idea at three levels. Answer yours; recognise the one above it.
Junior
"What is system design, and how is it different from the coding problems you've been doing?"
Model answer — "Coding problems ask me to write one correct, efficient function on one machine — there's usually a best answer. System design asks me to architect a whole service: the servers, database, cache, and how data flows between them, so it meets its features and its goals for scale, speed, reliability, and cost. The big difference is there's no single right answer — it's about trade-offs I have to justify for the given constraints."
Follow-up they'll throw: "Name the basic building blocks." → Client, load balancer, app servers, cache, database, message queue, workers, CDN — and say what each one is for.
Intermediate
"When would you keep a system as a simple monolith with one database instead of going distributed?"
Model answer — "As long as the whole workload fits comfortably on one machine and the traffic is served within my latency target, a monolith with one database is the right choice — it's cheaper, simpler, faster to build, and easier to debug, with no distributed-systems tax. I'd go distributed only when a specific number forces it: data too big for one disk, traffic beyond one server, or a need to isolate failure so one part can die without the rest. The cost of 'distributed' is complexity, money, and new failure modes, so I pay it only to remove a bottleneck I can actually measure."
Follow-up: "What's the first thing distributing buys you, and what does it cost?" → It buys horizontal scale and failure isolation; it costs network latency, partial-failure handling, and operational overhead.
Senior
"How do you approach a system design problem you've never seen before, and why is there 'no right answer'?"
Model answer — "I drive it with numbers and trade-offs, not with a template. First I clarify requirements — functional features, then the non-functional targets that actually make it hard: scale, latency, availability, durability, cost. Then back-of-the-envelope estimation — QPS, storage, bandwidth — because those numbers pick the architecture. I sketch the simplest design that meets them, then deep-dive the two or three hard parts and stress-test each for its bottleneck and its single points of failure. There's no single right answer because every choice trades one property for another — consistency for availability, latency for cost, simplicity for scale — so the 'best' design is the one whose trade-offs best fit these constraints. My job is to make those trade-offs explicit and defend them."
Senior insight: they're not grading the diagram — they're grading whether you reason from numbers, name trade-offs, and know what fails. State assumptions out loud, quantify, and revisit the design as constraints change.
Company lens — how the same topic gets asked
micro1Live and conversational — the AI interviewer wants to hear you reason out loud. Narrate the flow: clarify requirements, estimate, then draw. State each trade-off as you make it; thinking silently reads as not thinking.
GoogleRigor and scale. Expect deep numbers and "what happens at 10× / 100×?" Quantify QPS, storage, and latency, and be ready to defend every component's existence and its failure behaviour.
MetaProduct-scale and pragmatic. They favour designs grounded in a real product's read/write patterns — reason from how users actually behave, not from a generic template.
AmazonOperational excellence and cost. Expect "what does this cost to run?", "what's the blast radius when it fails?", and single-point-of-failure hunting — tie it to the Leadership Principles of Frugality and Ownership.
Hands-on exercise
  1. 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.
  2. 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.
  3. 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.
  4. 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.
Narrative bridge

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.