Numbers are the most important part of system design, and the most frequently skipped.

It’s easy to reach for a technology before understanding the problem. “We’ll use Kafka for the event stream.” “Redis for caching.” “Cassandra for scale.” These might be right — but without numbers, you don’t actually know. A system designed without numerical constraints is a system designed for an imaginary problem.

Latency, throughput, and storage are hard constraints for your system. They determine whether a single database can handle the load, whether you need a cache, whether you need to shard, and which storage engine is even viable. Get the numbers wrong and the architecture follows you off a cliff.


Step 1: Establish Constraints Before Designing Anything

Before sketching any architecture, answer these questions. Every one of them has a downstream consequence.

Scale

  • How many daily active users (DAU)?
  • How many requests per day, and of what type?
  • What is the peak-to-average ratio? (Traffic is never flat — a 10x peak means your average throughput figure is almost useless for capacity planning.)

Access patterns

  • What is the read-to-write ratio?
  • Are reads and writes uniform, or is access skewed (e.g. hot keys, celebrity users, viral content)?
  • Are operations simple key lookups, or complex queries?

Data

  • What is the average size of a record?
  • What is the data retention requirement? (30 days? Forever? This drives storage cost by orders of magnitude.)
  • Does data need to be strongly consistent, or is eventual consistency acceptable?

Latency

  • What is the acceptable response time at the 99th percentile? (Mean and median data points are not acceptable — they ignore half your traffic.)
  • Is this a user-facing read path (strict) or a background job (lenient)?

Availability

  • What is the acceptable downtime? (99.9% = ~8.7 hours/year. 99.99% = ~52 minutes/year.)
  • What happens if the system is unavailable — lost revenue, data loss, safety risk?

Once these are answered, you have a constraint set. The architecture is the solution to those constraints — not a collection of technologies you happen to know.


Reference Numbers Worth Memorizing

These are the rough figures that make estimation possible. You don’t need precision — you need order of magnitude.

Latency hierarchy

OperationApproximate latency
L1 cache access~1 ns
L2 cache access~4 ns
RAM access~100 ns
SSD random read~100 µs
HDD random read~10 ms
Network round-trip (same DC)~0.5 ms
Network round-trip (cross-region)~100–200 ms

The gap between RAM and disk is 5 orders of magnitude. The gap between in-datacenter and cross-region is ~200x. These numbers explain why caching exists, why databases keep hot data in memory, and why geographic distribution is hard.

Throughput / storage rules of thumb

UnitValue
1 million requests/day~12 requests/second
1 billion requests/day~12,000 requests/second
1 KB x 1 million users~1 GB
1 MB x 1 million users~1 TB
1 GB x 1 million users~1 PB
Peak ≈ Average x 3–10(use 5x as a starting point)

Scenario 1: Multiplayer Game Matchmaking

The system: A competitive online game with global players. Players queue for a match, the system finds compatible opponents (similar skill rating, low latency between players), and starts a game session. SLA indicates a maximum latency of 5 seconds.

Naive design: A single matchmaking service queries a SQL database for available players, runs a matching algorithm, and creates a session. Works fine in staging with 100 concurrent players.

The numbers:

DAU:                    10 million players
Average concurrent:     500,000 (5% of DAU online at once)
Peak concurrent:        2,000,000 (4x average)
Queue joins/sec:        ~50,000 at peak
Match latency SLA:      < 5 seconds
Session state size:     ~2 KB per active player
Total session state:    2,000,000 x 2 KB = ~4 GB

What breaks:

  • A SQL database scanning for available players across 2 million rows with a 5-second SLA doesn’t work. Index scans under write-heavy load degrade fast.
  • A single matchmaking service becomes a bottleneck at 50,000 queue joins/second.
  • A statically provisioned infrastructure does not handle a 4x traffic spike well. It either over-provisions at idle cost or falls over at peak.

What the numbers demand:

  • In-memory player pool — 4 GB of session state fits comfortably in Redis. Player availability and skill ratings are hot data; keep them out of disk-backed storage entirely.
  • Partitioned matchmaking workers — partition the player pool by region and skill bracket. Each partition is independently matchmade, reducing per-worker load and keeping players geographically close (latency between matched players matters for game quality).
  • Autoscaling queue workers — the peak-to-average ratio of 4x makes static provisioning wasteful. Scale workers horizontally against queue depth.
  • Consistent hashing for session affinity — a player reconnecting mid-match should reach the same session server. Consistent hashing pins player IDs to specific nodes without a central lookup.

The constraint that drives everything here is the 5-second SLA under a 4x burst. Every architectural decision traces back to it.


Scenario 2: Social Media Feed (Read-Heavy)

The system: Users follow other users. Each user has a feed of posts from accounts they follow. Target: 500 million DAU, average user reads their feed 10 times per day, posts 0.1 times per day.

Naive design: On feed load, query a posts table filtered by accounts the user follows, sorted by time. Works at 1,000 users.

The numbers:

DAU:                    500 million
Feed reads/day:         500M x 10 = 5 billion
Feed writes/day:        500M x 0.1 = 50 million
Read-to-write ratio:    100:1
Peak read QPS:          ~300,000 (5B / 86,400 x 5x peak)
Average post fanout:    500 followers per user
Write amplification:    50M posts x 500 = 25 billion feed insertions/day

What breaks:

  • A JOIN across follows and posts for 500 followers at 300,000 QPS destroys a relational database. Query latency collapses under concurrent load.
  • Even with indexes, real-time feed assembly at this scale is too slow — you’re aggregating 500 rows per request, 300,000 times per second.

What the numbers demand:

  • Pre-computed feeds (fan-out on write) — when a user posts, push the post ID into every follower’s feed cache immediately. Feed reads become a single cache lookup. Trade write amplification (25 billion insertions/day) for read simplicity.
  • Tiered fan-out for celebrity accounts — a user with 50 million followers can’t fan out synchronously on write (50M cache insertions per post). Hybrid approach: pre-compute for normal users, assemble on read for celebrity posts and merge at serve time.
  • Redis sorted sets for feed storage — feed is an ordered list of post IDs, scored by timestamp. Sorted sets give O(log N) insertion and O(1) range reads. 500 IDs x 8 bytes x 500M users = ~2 TB hot feed state — distributed across a Redis cluster.
  • CDN for media — images and video are never served from origin. At this read volume, bandwidth alone would be prohibitive without edge caching.

The read-to-write ratio of 100:1 is the constraint that mandates pre-computation. Every read needs to be a lookup, not a query.


Scenario 3: Metrics and Logging Pipeline (Write-Heavy)

The system: A platform that ingests application metrics and logs from thousands of services — CPU usage, error rates, request latency, custom events. Engineers query it occasionally for dashboards and alerts.

Naive design: Services write logs to a shared PostgreSQL table. Engineers run SQL queries for dashboards.

The numbers:

Services instrumented:      5,000
Events per service/sec:     200
Total ingest rate:          5,000 x 200 = 1,000,000 events/sec
Average event size:         500 bytes
Ingest throughput:          ~500 MB/sec (~1.7 TB/hour)
Retention requirement:      90 days
Total storage:              1.7 TB/hr x 24 x 90 = ~3.7 PB
Read QPS (dashboards):      ~1,000 (engineers querying occasionally)
Write-to-read ratio:        ~1,000:1

What breaks:

  • PostgreSQL at 1 million writes/second is not viable. B-tree indexes degrade under write-heavy load; VACUUM overhead compounds the problem.
  • 3.7 PB in a relational database is cost-prohibitive and operationally painful.
  • Transactional write paths (row locking, WAL, fsync) add overhead that event ingestion doesn’t need and can’t afford.

What the numbers demand:

  • Kafka as the ingest buffer — services write to Kafka topics, not directly to storage. Kafka absorbs the 1M events/sec write burst, decouples producers from storage, and allows replay if downstream consumers fall behind. Partitioned by service ID for parallel consumption.
  • Columnar storage — metrics data is wide (many fields) but queries typically touch few columns (“give me CPU usage for service X over the last hour”). Columnar formats compress well and allow column pruning — orders of magnitude faster than row-oriented reads for analytical queries. S3 at 0.023/GB makes 3.7 PB cost ~85K/month vs. multiples of that on block storage.
  • Time-based partitioning — partition data by hour or day. Queries almost always have a time range predicate; partition pruning eliminates scanning irrelevant data entirely.
  • Flink or Spark Streaming for aggregation — raw events are rarely what engineers need. Pre-aggregate into 1-minute rollups (avg CPU, p99 latency) so dashboards read from small aggregation tables rather than raw event logs.

The write-to-read ratio of 1000:1 inverts the usual design priorities. Optimize the write path ruthlessly; reads are infrequent enough to tolerate slightly higher latency.


Why This Matters in Interviews

System design interviews are not testing whether you know what Kafka is or that Netflix uses Cassandra. They’re testing whether you can reason from constraints to architecture.

An interviewer who hears “we’ll use Kafka and Redis and Cassandra” without prior estimation can’t tell whether you understand why those choices are appropriate or whether you’re pattern-matching to what you’ve read. The numbers are what justify the decisions.

The engineers who stand out are the ones who, before drawing a single box, ask: how many users, how often, how large, how long. Then they derive what the system must handle — and only then does the architecture follow.

This is also why studying systems is more valuable than studying tech stacks. Understanding why Facebook pre-computes feeds, why Dynamo resolves conflicts on read, why Cassandra uses an LSM tree — that understanding transfers. Knowing that they use these tools does not.


Further Reading