You have built Prowid Photos — an app where people post photos and follow each other. On your laptop and with ten friends everything flies. But what happens when a million people show up every day? Will one server be enough, and what if it goes down on a Friday evening? System design is the skill of answering such questions before they turn into outages. In this lesson we will write requirements down as numbers, get to know latency, throughput and the "nines" of availability, and learn to calculate "on a napkin".
What system design is
System design is choosing the parts of a system (client, API, database, cache, queue, file storage), how they talk to each other, and answering one question: what breaks first when the load grows. There are no perfect solutions here, only trade-offs: faster or cheaper, simpler or more reliable.
An analogy is a kitchen. A kitchen for a family of four and one for a 500-seat restaurant are designed differently, but first you need to know how many guests will come and what they will eat. That is why any system is analysed in four steps:
- Requirements — what the system must do and how well.
- Estimates — how many requests, how much data and traffic we expect.
- Components — what we build it from and how the parts are connected.
- Bottlenecks — what hits the ceiling first and how to scale it.
This lesson covers steps 1 and 2, the rest of the module covers steps 3 and 4.
Step 1. Requirements: what we build and how well
Functional requirements describe what the system can do: upload a photo, follow a user, like a post, open a feed. Non-functional requirements describe how well it does it: how fast the feed opens, how many hours a year the service may be down, how many users it can handle, whether photos can get lost. They are what shapes the architecture: "upload a photo" can be written over a weekend, but "upload a photo so that it is never lost, with a million users and 99.9% availability" is a task for months.
A typical beginner mistake is to phrase non-functional requirements in words: "fast", "reliable", "for many users". Such a requirement can be neither verified nor designed for. Turn every word into a number:
// Prowid Photos: requirements as numbers, not wishes
const functional = [
'upload a photo',
'follow other users',
'like and comment',
'open a feed of the people I follow',
];
const nonFunctional = {
feedLatencyP95Ms: 300, // 95% of feed requests are faster than this
availabilityPercent: 99.9, // about 8.8 hours of downtime per year
dau: 1_000_000, // daily active users
peakRps: 350, // the busiest second of the day
feedFreshnessSeconds: 10, // a new photo shows up within 10 s
photosAreNeverLost: true, // durability matters more than speed
};Questions to ask the customer (or yourself) before the first line of code: