System design

A practical guide to capacity, state, communication, and failure in backend systems.

In this article

System design is the work of choosing where state lives, how work moves, and what happens when a dependency is slow or unavailable. These pages build that model from first principles.

A useful reading path

Start with performance and scalability, then compare latency and throughput. Next, study availability and consistency and the consistency patterns that make those guarantees concrete.

The remaining pages describe the building blocks you combine in a request path:

Start with one request

Describe one user action and its result. Include expected traffic, the data that changes, and the time the user can wait.

For POST /uploads, that might mean 500 requests per second at peak, files up to 50 MB, a response within 300 ms, and processing that can finish later. Those facts rule out some designs before you draw a box.

Name the modules

Give each module a small interface and a clear owner.

client -> edge -> upload API -> object store
                         \\-> job queue -> processor -> metadata store

The upload API accepts the request and records intent. The object store owns file bytes. The queue separates the user response from processing time. The metadata store records state that the product can query.

Ask what can fail

For every dependency, ask what a slow response, a timeout, a duplicate message, and a total outage mean. Then decide whether to retry, fail, queue, or return a partial result.

The right answer depends on the operation. Retrying a read may be harmless. Retrying a charge can create a second payment unless the operation has an idempotency key.

For a deeper view of a single request, continue to the backend request lifecycle. For interface choices, see API design.

Keep track of your reading.

Your reading progress stays in this browser.