Part 2 · System Design Core Concepts

What Is System Design? Start With Requirements

Two teams build the same app; one collapses on launch day. The difference is not coding skill but system design — and good design always starts with requirements you can measure.

Lesson 068 min readbeginner#system-design#requirements

On this page

  1. The Problem
  2. The Core Idea
  3. How It Works
  4. Trade-offs
  5. In the Real World
  6. Interview Questions
  7. Key Takeaways
  8. Further Reading

The Problem

Imagine two teams are asked to "build a video streaming app".

  • Team A starts coding right away. Three months later they have a beautiful app. On launch day, 200,000 people join at once and it collapses.
  • Team B spends the first week asking questions: How many users? Live or recorded video? Which countries? What happens if a server dies? Their app looks simpler, but it survives launch day.

The difference is not coding skill. It is system design. System design is the work of deciding what to build and how the pieces fit together before writing lots of code. Good system design always starts in the same place, with requirements. A requirement is something the system must do, or a quality it must have.

The Core Idea

Think about building a house. Before anyone lays a brick, the architect asks:

  • How many people will live here?
  • Do you need a garage?
  • Is this an earthquake zone?
  • What is the budget?

The answers change everything. A house for two people in a calm area is very different from a 20-floor apartment block in an earthquake zone.

System design is architecture for software. Architecture means the main parts of a system and how they connect. You decide:

  • Which building blocks to use: servers (computers that answer requests), databases (long-term storage for data), caches (fast stores of saved answers), queues (lists of jobs that wait for their turn) and CDNs (servers around the world that keep copies of files near users).
  • How they connect.
  • Which trade-offs to accept. A trade-off means you gain one thing and lose another: speed vs cost, simplicity vs scale, consistency vs availability.

There is rarely one "correct" design. There are only designs that fit the requirements and designs that do not.

How It Works

Two levels of design

High-level design (HLD)
  • •Boxes and arrows: clients, load balancer, app servers, cache, database, queue
  • •Which building blocks, and how they connect
  • •Where data lives and how it flows
Use whenArchitecture reviews, system design interviews
Low-level design (LLD)
  • •The inside of one box
  • •Classes, database tables, API endpoints
  • •Algorithms and data structures
Use whenCode reviews, implementation planning
The two levels of detail in a design. Interviews and this series focus mostly on the left side. · compare
  • High-level design (HLD) is the big picture: boxes and arrows. "Clients talk to a load balancer (a server that shares requests between many servers), which talks to app servers, which use a cache and a database." This is what most system-design articles and interviews focus on.
  • Low-level design (LLD) is the detail inside one box: classes (code blueprints), database tables, API endpoints (the URLs that a service offers) and algorithms (step-by-step methods).

This series is mostly about HLD, but we go into LLD when it matters, for example database schemas (the layout of tables) and API design.

The qualities every system is judged on

When people say a system is "good", they usually mean some mix of these qualities. Each one is a question you can ask about a system:

QualityPlain-English question
ScalabilityCan it handle 10× or 100× more users?
AvailabilityIs it up when people need it?
ReliabilityDoes it give correct results, even when parts fail?
PerformanceIs it fast for each user?
ConsistencyDoes everyone see the same, up-to-date data?
MaintainabilityCan the team understand, change and fix it easily?
SecurityIs data protected from people who should not see it?
CostCan the business afford to run it?

You cannot get the best score in all of them at once. Improving one often hurts another. That is why design is about trade-offs. Two terms are easy to confuse: availability is whether the system is up and answering, and reliability is whether the answers are correct.

Performance vs scalability

People often mix these two up, so let us separate them:

  • A performance problem: the system is slow even for one user. Example: a page that takes 5 seconds because of a missing database index.
  • A scalability problem: the system is fast for one user but gets slower as users grow. Example: a page that loads in 100 ms with 10 users but 10 seconds with 10,000, because every request competes for the same database.

Fixing performance usually means better code or queries. Fixing scalability usually means changing the architecture. Scalability means how well a system keeps working as the load grows.

Step one: requirements

Requirements come in two types.

1. Functional requirements: what the system does. These are the features, written from the user's point of view. For a URL shortener (a service that turns a long web address into a short one):

  • A user can paste a long URL and get a short one.
  • Visiting the short URL redirects to the long URL.
  • A user can choose a custom alias (optional).
  • Links can expire after a set time (optional).

Just as important is what is out of scope (not part of the work). For example: "We are not building user accounts or analytics dashboards in version 1." Saying this out loud stops the design from growing forever.

2. Non-functional requirements (NFRs): how well the system does it. These are the qualities from the table above, and they drive most architecture decisions. Difference in one line: functional says what the system does, non-functional says how well it does it. Examples:

  • Scale: 100 million new links per month, 10 billion redirects per month.
  • Latency: redirects should feel instant, under 50 ms for most users. (Latency is the time one request takes.)
  • Availability: redirects must almost never fail. Creating links can accept a little downtime.
  • Durability: saved data stays saved. A link, once created, must never be lost.
  • Read/write ratio: about 100 redirects (reads) for every 1 link created (write), so it is very read-heavy.
Functional — what it does
  • •Paste a long URL → get a short one
  • •Visiting the short URL redirects to the long one
  • •Optional: custom alias, expiry date
  • −Out of scope in v1: accounts, analytics dashboards
Non-functional — how well
  • •Scale: 100 M new links and 10 B redirects a month
  • •Latency: redirects under 50 ms for most users
  • •Availability: redirects almost never fail
  • •Durability: a created link is never lost
  • •Shape: ~100 reads per write — very read-heavy
A URL shortener's requirements, split the way every design should split them. · compare

Make requirements measurable

"The site must be fast and always up" sounds like a requirement, but it is not one. You cannot design for it or test it. Turn vague words into numbers:

VagueMeasurable
"Fast""99% of API requests finish in under 200 ms" (this kind of number is called a percentile, such as p99)
"Always up""99.95% of requests succeed each month"
"Handles lots of users""5,000 requests per second at peak, growing 50% per year"
"Never lose data""Zero data loss if one server or one data centre fails"

We learn what these numbers mean in the next few lessons: latency percentiles, availability "nines" and estimation.

Constraints and assumptions

A constraint is something you cannot change. An assumption is something you guess is true. Write down both:

  • Budget and team size (three engineers cannot run 40 microservices, which are many small separate services).
  • Where users are (one country or worldwide?).
  • Legal rules (data about EU users may need to stay in the EU).
  • Deadlines.
  • Assumptions like "the average post is 1 KB" or "20% of users are active daily".

Assumptions are fine. Hidden assumptions are dangerous. Write them down so that others can question them.

Thinking in trade-offs

Almost every decision in this series is a trade-off. Here are the ones you will meet again and again:

  • Consistency vs availability: show slightly old data, or show an error? (Consistency means everyone sees the same, latest data.)
  • Latency vs throughput: answer each request quickly, or group work into batches to do more in total? (Throughput is how much work is done per second.)
  • Read speed vs write speed: keep extra copies of data to make reads fast, but make writes slower?
  • SQL vs NoSQL (tables vs other kinds of databases), monolith vs microservices (one big program vs many small ones), build vs buy.
  • Cost vs everything else: more servers, more regions (separate geographic locations) and more copies all cost money.

Reversible vs irreversible decisions. Some decisions are easy to undo, like changing a cache timeout. Others are very hard to undo, like choosing a database, a sharding key (the field used to split data across databases) or a public API format. Decide fast on the easy ones and think carefully about the hard ones.

Write decisions down. Many teams use short Architecture Decision Records (ADRs): one page saying what was decided, why, what other options were considered, and what trade-offs were accepted. Six months later, when someone asks "why did we pick PostgreSQL?", the answer is there.

Simple is a feature

Every new component (a cache, a queue, a new database) is one more thing that can break, one more thing to watch, and one more thing to learn. A design that meets the requirements with fewer parts usually beats a clever one. Many engineers call this "choosing boring technology". Well-known, well-understood tools let you spend your energy on the real product.

  1. 1
    Functional requirements
    the features, from the user's side — and what is out of scope
  2. 2
    Non-functional requirements
    scale, latency, availability, durability, cost — as numbers
  3. 3
    Constraints and assumptions
    budget, team size, regions, laws, deadlines — written down
  4. 4
    Estimate
    rough numbers for QPS (queries per second), storage and bandwidth
  5. 5
    High-level design
    the fewest boxes and arrows that meet the numbers
  6. 6
    Trade-offs
    what you gave up, and why — recorded in an ADR
The order to think in, for any design — from a blank page to a design you can explain and defend. · flow

Trade-offs

  • Spending time on design first prevents expensive rewrites, but too much design delays learning from real users. For small projects, a one-page design is often enough.
  • Designing for huge scale too early wastes time and money, and adds complexity that you do not need yet. Design for your realistic next 12–24 months, with a way to grow later.
  • Fixed requirements make decisions easier but can be wrong. Look at them again as you learn.

In the Real World

Twitter's "Fail Whale". In its early years, Twitter was known for an error page that showed a whale lifted by birds. It appeared whenever the site was overloaded. Twitter was a huge success as a product, but its first design had not planned for how fast it would grow or for traffic spikes during big events. Over several years the company redesigned major parts of its system. This is a classic example of non-functional requirements (scale and availability) catching up with a product.

Live cricket streaming in India. Streaming platforms showing big cricket matches have to handle tens of millions of people watching at the same moment, with sudden jumps when a star player comes in to bat or a match gets close. Their requirement is not "handle average traffic". It is "handle the biggest spike of the year without failing". This one non-functional requirement shapes their whole architecture: heavy use of CDNs, adding servers before the match instead of waiting for automatic scaling, and turning off non-essential features when the load is very high.

Amazon's "one-way vs two-way doors". Jeff Bezos described decisions as either two-way doors (easy to reverse, so decide quickly) or one-way doors (hard to reverse, so decide carefully). Many tech companies use this idea when they review designs.

Design docs at big companies. Google, Uber and many others write a short design document before building any big system. It describes the goals, non-goals, proposed design, other options that were considered, and trade-offs. Colleagues review it before coding starts. The "non-goals" section, which says what they are not doing on purpose, is often the most useful part.

Interview Questions

Q1What is the first thing you do when asked to design a system?

Ask questions before drawing anything. Agree on the core features (and what is out of scope). Then get the non-functional requirements as numbers: users, requests per second, read/write ratio, latency and availability targets, data size and regions. Also ask about constraints like budget and team size.

Q2What is the difference between functional and non-functional requirements?

Functional requirements describe what the system does: the features and user actions. Non-functional requirements describe how well it does them: scale, latency, availability, durability, security and cost. The non-functional ones drive most architecture decisions.

Q3Performance problem or scalability problem — how do you tell?

If it is slow for a single user, it is a performance problem: usually code, queries or a missing index. If it is fast with few users and gets worse as load grows, it is a scalability problem: usually a shared bottleneck (one part that everything waits for) that needs a change in the architecture.

Q4Why write down non-goals and assumptions?

Non-goals stop the scope from growing, and they give reviewers a clear target. Written assumptions (“1 KB per post”, “20% of users active daily”) can be questioned and checked. Hidden ones quietly shape the design and show up later as outages.

Q5What is a one-way door decision? Give examples.

A decision that is expensive or impossible to reverse, so it needs careful thought: the main database, the shard key, the public API format, the data model. Two-way doors can be decided quickly and changed later. Examples are cache TTLs (how long a cached item lives), instance sizes (server sizes) and feature flags (switches that turn a feature on or off).

Key Takeaways

  • System design means choosing components and how they connect to meet requirements, with clear trade-offs.
  • Functional requirements say what the system does. Non-functional requirements (scale, latency, availability, durability, cost) drive the architecture.
  • Make requirements measurable ("p99 < 200 ms", "99.95% monthly"), and state what is out of scope.
  • A performance problem is slow for one user. A scalability problem gets worse as users grow.
  • Prefer simple designs, move fast on reversible decisions, and write down the hard ones.

Further Reading

  • Designing Data-Intensive Applications by Martin Kleppmann (chapter 1, "Reliable, Scalable, and Maintainable Applications")
  • System Design Interview: An Insider's Guide by Alex Xu (chapter 1, "Scale From Zero To Millions Of Users")
  • The System Design Primer on GitHub (section "Performance vs scalability")
  • Michael Nygard's article "Documenting Architecture Decisions"
  • Dan McKinley's essay "Choose Boring Technology"

Published by Jainik · Last updated · How these lessons are made

Keep learning

Part of the study guide

Related series