The system design interview mental model
Every system design exercise — whether it's an interview or a real design doc — falls apart in the same place: someone starts drawing boxes before anyone agrees what the system is supposed to do, at what scale, and under what constraints. The discipline that separates a strong design from a shaky one is almost entirely upstream of the architecture: clarify requirements, do the arithmetic, and only then let the numbers tell you which trade-offs are even on the table.
Functional vs. non-functional requirements
Functional requirements describe what the system does — the operations a user or client can invoke, and their expected behavior. Non-functional requirements describe the qualities the system must have while doing that — scale (how many users, how much data, how much traffic), latency (p50/p99 targets), availability (can it tolerate downtime, and how much), consistency (can reads be briefly stale), and durability (can data ever be lost). Non-functional requirements are the ones that actually drive architecture: two systems with identical functional requirements — "let a user post a message" — produce completely different designs if one needs to serve 500 requests a day and the other needs to serve 500,000 a second with a 200ms p99.
A few clarifying questions worth asking for almost any "design X" prompt, because the answers change the design:
- What's the read:write ratio? A read-heavy system (Twitter timelines, a URL shortener) optimizes around caching and replication; a write-heavy system (metrics ingestion, an order pipeline) optimizes around ingestion throughput and buffering.
- What's the scale, roughly — daily active users, requests per user, total data volume? Even an order-of-magnitude estimate changes whether a single database instance is fine or sharding is mandatory from day one.
- What's the latency requirement, and is it a hard real-time constraint or a "should feel fast" constraint? A single-digit-millisecond p99 rules out synchronous cross-region calls in the hot path; a 2-second budget does not.
- Does correctness matter more than availability, or the reverse, if the network partitions? A payments ledger and a "like" counter answer this question differently, and that answer determines the consistency model.
- Is this a green-field system, or does it need to interoperate with, or migrate from, something that already exists in production?
Back-of-envelope estimation as a discipline
Estimation isn't a box-ticking ritual before the "real" design work — it's the mechanism by which requirements become constraints you can design against. The method is always the same three calculations: traffic (QPS), storage growth, and bandwidth, derived from a small number of assumed inputs using simple arithmetic. A few approximations make the mental math fast and are worth memorizing:
Say we're estimating for a hypothetical service with 10 million daily active users (DAU), where each user performs 20 read actions and 2 write actions per day on average, each request/response pair is roughly 2 KB, and each written record is roughly 1 KB. These numbers are illustrative, not observed — the point is the method, which works the same way regardless of the actual inputs.
The value of doing this isn't the specific numbers — it's what they rule in or out. A ~7,000 peak QPS read path with a 2-3x read:write skew says "you need a cache and probably read replicas, but you do not need to shard on day one." A p99 latency budget of 10ms says "no synchronous call to a service in another region can be in the hot path, full stop" — the speed of light alone (roughly 60-70ms round-trip cross-continent over real network paths) makes that arithmetically impossible before any processing time is added. This is the actual purpose of estimation: it converts vague scale talk into hard constraints that eliminate whole categories of design, before you've drawn a single box.
Back-of-envelope capacity calculator
InteractivePlug in rough numbers for a hypothetical service and watch the QPS, storage, and bandwidth arithmetic compute live — the same three calculations from above, worked on your inputs.