skip to content

learn.smmeyer.devsystem-design01

system-design/01

foundations · core · ~3h practice

System design foundations

outcome

Turn an ambiguous prompt into a scoped, quantified design conversation.

Plan a practice session · ~3h including exercises
Read + trace the worked example
25 min
Assignment + constraint variations
100 min
Timed drill + repeated explanations
35 min
Closed-book recall + self-review
20 min

This is a suggested practice budget, including drawing, variations, and repeated drills. Reading is the first block. Split the work across sessions and use the rubric to judge when to move on.

why this matters

The first ten minutes often reveal more seniority than the diagram. Strong candidates turn ambiguity into explicit decisions, use estimates to find the real bottleneck, and keep the design proportional to the problem.

coverage

  • Requirements: separate must-have user behavior from constraints and future wishes.
  • Quality attributes: latency, throughput, availability, durability, consistency, cost, and security become design inputs.
  • Estimation: convert users and behavior into peak RPS, bytes, bandwidth, memory, and growth.
  • Decomposition: map clients, APIs, services, stores, queues, caches, and workers to responsibilities.

mental model

Move from fuzzy prompt to a defended first design.

  1. 1 →Clarify

    Users + workflows

  2. 2 →Quantify

    Scale + SLOs

  3. 3 →Decompose

    Boundaries + data

  4. 4 →Stress

    Bottlenecks + failure

Read in numbered order: clarify → quantify → decompose → stress.

lessons

01Start with the contract

Ask questions that change architecture: who uses it, what the critical read/write path is, whether data loss is acceptable, and what success looks like. State assumptions when the interviewer will not choose for you.

Consider a paste-sharing service. “Create a paste” sounds simple until you ask whether pastes are public, whether readers need accounts, and whether an expired link may still reveal a cached body. Each answer changes a different boundary: authorization, key design, retention, or cache behavior. Write a concrete success trace before naming components: the creator submits text, receives a stable URL, and an authorized reader can retrieve it until expiry.

Turn vague quality goals into testable statements. “Fast reads” might become “95% of reads finish within 200 ms, measured at the service edge.” Specify the population and window, and whether failures count. A durability requirement asks what acknowledged data may be lost; an availability requirement asks whether requests can finish. You can have a reachable system that loses accepted writes, so neither requirement substitutes for the other.

Separate a known requirement from an assumed one. Record “assume one-year retention; confirm with product” rather than quietly designing around it. Pick the two constraints that dominate the first design. If privacy and reliable retrieval matter more than global latency, spend your early reasoning on permission checks and acknowledged storage instead of worldwide replication.

  • Functional: verbs the system must support
  • Non-functional: measurable quality constraints
  • Out of scope: complexity you deliberately defer

02Estimate to choose—not to perform arithmetic

Use round numbers and show units. An estimate matters when it changes a decision: one database or shards, synchronous work or a queue, memory cache or disk, one region or several.

Build estimates from behavior. Suppose 5M daily readers each make three reads: 15M reads/day divided by 86,400 seconds/day is about 174 reads/s. A stated 5× peak gives 868 reads/s. MAU alone cannot supply either number: you need a daily-active share and actions per active user. Keep these assumptions next to the arithmetic so another engineer can challenge them.

Use units to catch mistakes. At 50k new 4 KB pastes/day, raw body growth is 200 MB/day, or 73 GB per 365-day year in decimal units. Three replicas make 219 GB before metadata, indexes, backups, and storage overhead. Replication increases retained bytes; it does not multiply the number of user creates. Peak bandwidth depends on response bytes and peak reads, not annual storage.

Finally ask which estimate changes a choice. A few writes per second suggests testing one relational primary before sharding. A 20 MB maximum body suggests separating blob transfer from metadata even if average objects are small. Recalculate with the daily-active share halved and doubled. If the decision stays the same, the estimate is already useful; extra decimal places will not resolve uncertainty in the workload.

  • Average RPS = daily actions / 86,400
  • Peak is commonly 2–10× average; explain your factor
  • Storage/year = writes/sec × bytes/write × seconds/year × replication

03Architecture is a chain of responsibilities

Every box should own something and every arrow should carry a named request or event. Begin with the critical path, then add components only to satisfy a requirement or repair a bottleneck.

Trace the write path first: client → API → authorize and validate → commit paste metadata and body → acknowledge. For each arrow, say what is carried and what happens if it times out. If metadata and bodies live in separate stores, ask how a reader avoids seeing metadata for an absent body. One simple option is to upload the immutable body first, then publish its metadata, with cleanup for abandoned uploads.

Trace the read path separately: parse ID → look up metadata → check permission and expiry → fetch body → respond. A cache is an optimization around this contract. Caching a public body is different from caching an authorization decision; a formerly authorized reader must not retain access merely because a cache entry remains warm. Name the authoritative state and where freshness is enforced.

Only now add scale mechanisms. If a load test shows the primary is saturated by repeat reads, evaluate caching or replicas; if writes or data exceed its measured limit, evaluate partitioning. A queue is justified when work can finish later and its eventual result has a clear contract. This method produces a small design whose next component has a reason to exist and a measurable trigger.

  • Name data and control flows
  • Mark stateful boundaries
  • Identify one bottleneck before proposing scale mechanisms

worked example

Size a paste service before choosing its storage

Assume 20M MAU, 25% active daily, three reads per daily user, 50k new pastes per day, 4 KB bodies, and a 5× read peak.

  1. Daily readers = 20M × 0.25 = 5M. Fifteen million reads/day gives about 174 average reads/s, or 868/s at the assumed peak.
  2. Creates average 50,000 / 86,400 = 0.58/s. With the same peak factor, provision for roughly three creates/s before accounting for bursts.
  3. Bodies add about 200 MB/day, or 73 GB/year using decimal units. Three replicas make 219 GB; indexes, metadata, backups, and overhead are extra.
  4. Draw create → metadata transaction → body storage, and read → lookup → body fetch. State expiry and private-paste access rules on both paths.

Design decision: A single relational primary is a plausible starting point for metadata. The estimates justify measuring its real workload before introducing shards.

Limit: A low average write rate does not prove capacity. Burst shape, body-size variance, indexes, durability settings, and measured query latency still matter.

Teaching scenario · traffic, prices, and capacity values are illustrative.

lab

interactive lab

Scale Dial — from monthly behavior to the bottleneck

1 · predict first

20M monthly users at 3 actions a day: what peak RPS? Commit to a number — and to the DAU share you assumed — before touching the dials.

A guess makes the comparison useful. This lab draft lasts until you leave the page.

pedagogical model — values are illustrative, not measured.

assignment

Produce: A one-page design with assumptions, an estimate table, two API shapes, and a labeled request diagram.

Paper, a text editor, or a spreadsheet is enough. Model access and paid services are optional.

  1. Calculate reads, creates, bandwidth, and retained bytes with units; explain which number could change your design.
  2. Draw a minimal v1 and trace one successful create, one read, and an expired private paste.
  3. Spend a separate pass removing boxes that satisfy no stated requirement; present the result aloud and revise it.

Change a constraint

  • Increase the largest paste from 4 KB to 20 MB and introduce a viral hot paste.
  • Repeat the design with 1% DAU/MAU, then 50%; explain which assumptions dominate.

Self-review rubric

  • MAU becomes daily behavior through an explicit assumption.
  • Every box owns a responsibility and every arrow names data.
  • The sharding decision names a measured limit or a trigger for revisiting it.

If a criterion is missing, revise that part of the artifact before rating your practice below.

Compare a sample artifact after your attempt

One defensible answer under these assumptions. Compare the reasoning and checks with your work; other designs can satisfy the same contract.

Use the worked example's 20M MAU, 25% daily-active share, three reads/day, 50k creates/day, 4 KB bodies, and 5× peaks. Assume private access and one-year retention.

Estimate table (decimal units)
Reads: 15M/day → 173.6/s average → 868/s peak
Creates: 50k/day → 0.58/s average → 2.9/s peak
Peak body bandwidth: 868 × 4 KB ≈ 3.47 MB/s
Bodies: 200 MB/day → 73 GB/year → 219 GB at 3 replicas

POST /pastes {body, visibility, expiresAt} → {id, url}
GET /pastes/:id → body, or denied/expired/not-found
Create: client → API → body upload → metadata commit → acknowledge
Read: client → API → owner/expiry check → body fetch → response

Metadata owns identity, owner, visibility, expiry, and body reference. Uploading an immutable body before publishing metadata avoids pointing a successful read at an unfinished upload; a cleanup job removes abandoned bodies. One relational metadata primary is a candidate to benchmark, not a capacity guarantee. Keep expiry and access checks on the read path.

Check your artifact

  • At 1% versus 50% DAU/MAU, peak reads become about 35 versus 1,736/s with the same behavior assumptions.
  • A 20 MB maximum body changes transfer and storage design even if typical bodies remain small.
  • An expired private paste never becomes readable through the body URL; revisit sharding only after workload measurements identify a limit.

A tempting mistake: Sharding because the prompt says 20M users skips the behavioral estimate. Show the peak workload and measured storage/query constraint first.

cumulative practice

Relay · build and evolve a private file service

Project brief

Keep one design document for Relay, a tenant-aware file service. Start with 100k daily users, ten downloads per user/day, 10k uploads/day, 1 MB typical bodies, 30-day retention, and a 5× download peak. Files are private to workspaces. Treat these as fictional planning assumptions. Later chapters add previews, recovery, regional constraints, compute, and an assistant. Record each change rather than replacing the earlier version; the final review combines the whole packet. These activities share the chapter's existing practice budget.

Keep one project document across this track. Reconstruct earlier reasoning before opening notes, then revise the same artifact. Use part of the assignment and review budget for this work. If you enter mid-course, sketch the earlier decisions first using the linked chapters.

Add to your project: Relay v1: requirements, an estimate table, and upload/download traces.

Start the project document from the brief. Without copying this chapter's arithmetic, derive daily and peak download RPS, average upload RPS, and 30-day raw body storage. Choose a minimal design and name one fact you still need to measure.

Compare cumulative reasoning after your attempt

100k daily users × ten downloads = 1M downloads/day: 11.57/s average and 57.9/s at 5× peak. Uploads average 10k/86,400 = 0.116/s. At 1 MB each, 30 days retains 300 GB raw, or 900 GB for three replicas before overhead. Start with authenticated metadata access and blob storage. Measure actual transfer sizes and query latency before adding a distributed metadata layout.

Keep your first attempt and revision together. If your answer differs, identify the assumption or mechanism that explains the difference before rating your recall.

senior signal

Say: “I’m estimating because this determines whether a single relational primary is plausible.” Tie every number and component to a decision.

failure patterns

  • Listing components before agreeing on the product contract
  • Inventing massive scale without estimating it
  • Optimizing secondary paths before the critical one
  • Treating every quality attribute as equally important

knowledge checks

Start with these quick checks, then explain the closed-book questions in your own words.

  1. 01Which estimate most directly tests whether sharding is needed?

    select an option to check your answer

  2. 02What belongs in the first five minutes?

    select an option to check your answer

closed-book recall

Close the worked example and explain these from memory. Answers are self-reviewed. Drafts stay on this page; save anything you want to keep in chapter notes.

Rate the assignment and recall together

Use the assignment rubric as evidence. Completion is your own judgment; this score schedules practice.

interview drill

Design a paste-sharing service used by 20M monthly users. In 35 minutes, scope v1 and decide whether one relational database is enough.

  • 35 min
  • 20M MAU
  • 1% creators
  • 1-year default retention
compare a strong approach after your attempt

Clarify privacy and paste size. Estimate peak creates and reads. Define create/read APIs and paste metadata. Put bodies in object storage if size variance is large; keep metadata and ownership relational. Add a CDN/cache only after quantifying the read ratio. Close with hot-link abuse, expiry, and failure behavior.

Compare decisions and failure behavior, then repeat with a changed constraint. Keep your own wording.

Use the timed mock workspace →

references

Sources offer deeper context. Provider capabilities and API contracts can change; check current official docs before implementing.

notes

saved locally