skip to content

learn.smmeyer.devsystem-design02

system-design/02

foundations · core · ~3h practice

Networking, APIs & boundaries

outcome

Choose protocols, boundaries, and traffic controls from interaction shape—not habit.

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

At scale, latency budgets disappear across hops, retries multiply load, and weak boundaries spread failure. Interviewers want practical protocol intuition and disciplined ownership.

coverage

  • HTTP in practice: HTTP/1.1, HTTP/2 multiplexing, HTTP/3, keep-alive, timeouts, retries, and idempotency.
  • API styles: REST resources, RPC/gRPC contracts, SSE streams, WebSockets, and pagination.
  • Service boundaries: monolith vs microservices, domain ownership, cohesion, coupling, sync vs async.
  • Traffic control: L4/L7 load balancing, health checks, regional routing, token/leaky buckets, tenant quotas.

mental model

A request is a latency and failure budget moving across ownership boundaries.

  1. 1 →Client

    Intent + retry

  2. 2 →Edge

    Route + limit

  3. 3 →Service

    Validate + own

  4. 4 →Dependency

    Store or compute

Read in numbered order: client → edge → service → dependency.

lessons

01Protocol follows conversation shape

REST is excellent for public resource APIs. gRPC fits typed internal request/response and streaming. SSE is simple server-to-client streaming over HTTP. WebSockets fit bidirectional, long-lived interaction. HTTP/2 reduces connection pressure; HTTP/3 improves behavior under packet loss.

Choose a transport by drawing a conversation. A browser asks for an existing invoice and gets one response: ordinary HTTP works. A report streams progress from server to browser: SSE can carry named events over an HTTP response. A collaborative editor sends edits in both directions throughout a session: a WebSocket may fit. These choices describe communication shape; they do not establish delivery, replay, or authorization guarantees.

A stream needs an application protocol. Give events an ID, type, payload, and explicit completion or error meaning. After a disconnect, can a client resume from the last event, fetch a durable result, or only restart? Sending a final text fragment is not the same as committing the operation. If a server has already performed a side effect, reconnecting must recover that operation rather than silently create another.

Connection reuse and multiplexing can reduce setup costs, but a protocol upgrade cannot remove a slow database query or an overloaded worker. Include DNS, connection setup, proxy buffering, and downstream processing in a latency trace. Measure the path you actually deploy, because a locally smooth stream can arrive in buffered bursts behind an intermediary.

  • Prefer the least stateful protocol that meets the need
  • Streaming changes cancellation, backpressure, and observability
  • Connection reuse matters before protocol trivia

02Retries are load amplifiers

A timeout does not prove failure; it proves the caller stopped waiting. Retrying a non-idempotent operation can duplicate side effects. Bound retries, add jitter, propagate deadlines, and use idempotency keys at the effect boundary.

A timeout means you stopped waiting; it does not tell you whether the remote effect occurred. A payment might commit before its response is lost. Retrying with a new identity then creates another payment. Define an idempotency key scoped to the caller and operation, bind it to the request parameters, and save the result at the effect boundary. Reusing a key with different parameters should be rejected.

Allocate one end-to-end deadline. With 800 ms total, 100 ms already spent, a 300 ms failed attempt, 50 ms backoff, and 100 ms reserved for response handling, only 250 ms remains for another attempt. Pass a remaining duration downstream. Independent full timeouts at each layer can make a supposedly bounded request continue long after the caller has left.

Retry only failures that might improve and only while budget remains. Use backoff with jitter to avoid synchronized retry waves, and limit concurrency as well as attempt count. If three layers each make up to three attempts, the leaf may receive 27 calls. Choose one retry owner where possible; otherwise coordinate a shared budget. Cancel work on disconnect when the operation contract permits it.

  • Retry transient failures, not every 4xx
  • Budget attempts inside the user deadline
  • Deduplicate at the component that commits the effect

03Boundaries are organizational and operational

Split when a domain needs independent ownership, scaling, security, or release cadence. Each synchronous hop increases latency and correlated-failure surface, so keep the critical path short.

A service boundary owns a contract, data, deployment behavior, and an on-call burden. Splitting an application into five services creates five independent failure opportunities and several network calls. Start by grouping responsibilities that must change or commit together, then split when ownership, scaling, security, or fault isolation justifies the operational cost. A module boundary is often enough initially.

Define compatibility before independent deployment. Adding an optional response field is usually easier to roll out than renaming a required field. Deploy readers that tolerate the new representation before writers depend on it. For streams, version event types and specify how clients treat unknown events. An internal API also needs compatibility rules if its clients and server deploy at different times.

Load balancing distributes requests among eligible backends; it cannot make every backend healthy or every request equally expensive. Readiness should reflect whether a process can serve new work, while overload admission can refuse work before queues grow without bound. Record request IDs across boundaries so you can connect the client-visible failure to the dependency attempt that caused it.

  • High cohesion inside, low coupling across
  • Prefer stable contracts over shared tables
  • Do not begin with microservices as a status symbol

worked example

Fit retries inside a request deadline

An API has an 800 ms user deadline. Authentication consumes 50 ms, orchestration 50 ms, and the first dependency attempt times out after 300 ms.

  1. The first attempt leaves 400 ms. Reserve 100 ms for response handling and 50 ms for backoff; a second attempt may use at most 250 ms.
  2. Carry the remaining deadline downstream. Do not let the second attempt start another independent 800 ms clock.
  3. If this is a create-message call, persist an idempotency key with the committed message. A timeout may have happened after the write succeeded.
  4. Use an HTTP event stream for incremental server output; explicitly propagate disconnects so abandoned generation stops consuming resources.

Design decision: Own retries at one boundary, cap their time and count, and deduplicate where the effect is committed.

Limit: The numbers are an exercise budget. Real deadlines must include network variation and connection setup; SSE alone does not provide exactly-once delivery.

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

assignment

Produce: An API contract and three sequence diagrams: success, timeout-after-commit, and client disconnect.

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

  1. Allocate a deadline across edge, service, and dependency; show a retry that fits and one that must be refused.
  2. Define request IDs, idempotency-key scope and retention, stream event shapes, cancellation, and terminal error behavior.
  3. Compare SSE and WebSockets for this workload, then rehearse the contract with a peer or a recorded explanation.

Change a constraint

  • Add a bidirectional voice session; reconsider the transport.
  • Let both the client and service retry three attempts; trace the resulting downstream load.

Self-review rubric

  • All attempts fit inside the user deadline.
  • The timeout-after-commit case returns or recovers the original result.
  • A disconnect has explicit cancellation and accounting behavior.

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.

The user deadline is 800 ms. Authentication and orchestration consume 100 ms; the first dependency attempt consumes 300 ms. This is a message-create API with a streaming result.

POST /messages; Idempotency-Key scoped to tenant + operation
Stored identity: key → request hash + message ID + status
Event: {eventId, operationId, type, payload}
Success: authorize → commit message + key → emit result → complete
Lost response: commit → timeout → retry same key → original result
Disconnect: stop stream → cancel cancellable generation → retain status
Retry budget: 800 - 100 - 300 - 50 backoff - 100 response = 250 ms

SSE fits one-way incremental output; the API owns replay or status recovery. The write's deduplication record commits with the message. Reusing a key with changed content fails instead of creating an unrelated effect. Voice may justify a bidirectional media/session transport; it does not change the operation identity rule.

Check your artifact

  • The second attempt cannot start a fresh 800 ms budget.
  • Three attempts at each of three layers can produce 27 leaf calls; choose one retry owner or share the budget.
  • Closing the browser stops expendable work, while a committed message remains inspectable by its original operation ID.

A tempting mistake: Treating a missing response as a failed write can duplicate a message. Inspect durable operation status before issuing another effect.

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's upload contract and deadline/reconnect sequence.

Recall which parts of Relay are metadata versus body transfer. Give an upload a stable operation ID, explain an ambiguous metadata-commit timeout, and allocate an 800 ms metadata deadline after 100 ms edge work, a 300 ms attempt, 50 ms backoff, and 100 ms response reserve. Large body transfer has its own explicit contract.

Compare cumulative reasoning after your attempt

Only 250 ms remains for a second metadata attempt. Persist operation identity with the metadata change; retrying the same parameters returns the same file ID. A body transfer can finish before metadata publication and later be cleaned up if abandoned. Reconnection fetches existing upload status; it does not create another file. The large transfer is not assumed to fit the short metadata deadline.

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

Discuss deadline propagation and retry ownership. “Three layers each retrying three times can turn one request into 27 downstream attempts.”

failure patterns

  • Using WebSockets for one-way token streaming when SSE suffices
  • Retrying writes without an idempotency contract
  • Confusing HTTP/2 multiplexing with unlimited concurrency
  • Drawing service boundaries around technical layers instead of domains

knowledge checks

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

  1. 01Best default for one-way incremental text to a browser?

    select an option to check your answer

  2. 02Where should write deduplication happen?

    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 the public API edge for a streaming AI chat product with per-workspace quotas and regional routing.

  • SSE output
  • 10k concurrent streams
  • tenant quotas
  • 60s max
compare a strong approach after your attempt

Authenticate at the edge, attach tenant identity, reserve quota before expensive work, route to a healthy region, and stream typed events. Propagate cancellation and deadlines. Make create-message idempotent, separate connection limits from token budgets, and define what a partial stream means for billing and retry.

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