Lesson15 min

Requirements & Smart Estimation

Extracting functional and non-functional requirements with clarifying questions, and doing capacity math only when it actually changes a decision.

Requirements are the contract you will be graded against

The first five minutes of the interview define the scope of everything after. If you skip this and start drawing, you are designing a system, not the system the interviewer had in mind — and mid-level candidates are graded heavily on whether the final design satisfies the requirements they set. This phase is load-bearing. Do it out loud, as a dialogue.

Functional requirements: "users should be able to..."

Functional requirements are the features — statements of what users can do. The technique is to propose the core set yourself and confirm it, rather than interrogating the interviewer with twenty questions:

"Users should be able to post tweets, follow other users, and see a chronological feed — I'll focus on those three. Does that match what you had in mind?"

Two rules:

  • Cap it at the top 3 (occasionally 4) core features. A long feature list actively hurts you: it multiplies the surface you must design in the same 45 minutes, and it signals you cannot prioritize.
  • Explicitly park things out of scope. "Does the system need to handle DMs, or is that out of scope?" Getting a "no" on the record is as valuable as a "yes" — it is scope you no longer owe.

Non-functional requirements: "the system should be..."

Non-functional requirements describe qualities, not features — and the mid-level bar is to make them quantified and specific, because vague ones ("it should be fast and scalable") decide nothing. The checklist worth running every time:

  • Consistency vs availability, per feature. The CAP theorem says that during a network failure between nodes, a distributed system must pick between consistency (everyone sees the same data) and availability (everyone gets an answer). The real question is: can this feature tolerate a stale read? Say it per feature: "Bookings need strong consistency — no double-sold seats — but event browsing can be eventually consistent." Treating consistency as one global switch is a known mid-level tell.
  • A latency target with a number. "Let's target under 500ms for feed loads" — not "low latency." The number becomes the pass/fail test for your read path in the deep dives.
  • Availability. Know what the nines cost: 99.9% is ~8.7 hours of downtime a year; 99.99% is ~52 minutes; 99.999% is ~5 minutes.
  • Scale assumptions. State the daily active users (DAU), the read:write ratio, and any data-size expectations. You are not calculating yet — you are putting numbers on the record to calculate from later.

Estimation: only when it changes a decision

Here is the modern guidance, and it surprises people: do not perform a ritual math phase up front. Ten minutes of ceremonial arithmetic that ends in "...so, lots of traffic" is a time-management failure. Instead, announce a policy:

"I'll hold off on detailed math and run numbers when they actually affect a decision — is that okay?"

This tells the interviewer you can do the math and you know when it matters. Then, at the exact moment a number determines a design choice, do that one calculation:

"Quick check: 100M DAU × 1 feed load ≈ 1,200 QPS average, maybe 5k peak — a cached read path handles that comfortably."

"500M URLs × 500 bytes ≈ 250GB — that fits on a single Postgres instance, so no sharding needed for storage."

Notice the shape: the output of each calculation is a decision (cache is enough; no sharding), never a bare number.

The arithmetic itself

Round aggressively — you only need to know whether you are 10x under or 10x over a limit.

Traffic: DAU × actions per user ÷ 86,400 seconds per day ≈ average QPS (queries per second). Round a day to ~100,000 seconds: 1M requests/day ≈ 12 QPS, 100M/day ≈ 1,150 QPS. Then state a peak multiplier: peak ≈ 2-5x average (assume at least 2x).

Storage: rows × row size × retention. Useful object sizes: a UUID is 16 bytes, a tweet ~280 bytes, a photo 200KB-2MB, one minute of HD video 50-100MB. Powers of two anchor the units: 2^30 ≈ 1GB, 2^40 ≈ 1TB.

Read:write ratio: tells you what kind of system you are building. 1000:1 (URL shortener) means cache everything; write-heavy (driver location pings) means think about queues and write-optimized stores.

Capacity anchors: what one machine handles

The math is useless without reference points to compare against. These are the numbers that turn arithmetic into decisions:

ResourceRough capacity
One Postgres instance~50k simple transactions/sec; a few TB comfortably
One Redis instance100k+ ops/sec; ~1ms per cache hit (vs 20-50ms for a DB query)
One Kafka broker~1 million messages/sec
One app server100k+ concurrent connections
Same-datacenter round trip~0.5ms
Cross-continent round trip~150ms

The single most valuable habit these enable: not distributing prematurely. A well-tuned single Postgres with read replicas handles far more than most candidates think — do not propose sharding below tens of TB or ~10k sustained writes/sec. Doing the one calculation that proves "this fits on one database" is high-signal; reflexively sharding a 250GB dataset is a classic over-engineering ding.

Worked example, end to end

Design prompt: a Twitter-like feed. Requirements phase output, spoken in about ninety seconds:

  • Functional: post tweets, follow users, view a chronological feed of followed users. DMs and search out of scope (confirmed).
  • Non-functional: availability over consistency — a feed that is a minute stale is fine; feed loads under 500ms; scale assumption 100M DAU, read-heavy (each user reads far more than they post).
  • Estimation policy announced; one inline check later: 100M DAU × 1 feed load ≈ 1,200 QPS average, ~5k peak — read path must be cached, but this is not exotic scale.

That is the whole phase. Every sentence either scoped the work or created a testable target the deep dives will be checked against.