System design
Design systems the way interviews expect: start from what the system must do, then add only the parts those requirements need.
Three kinds of design question
| Kind | What it asks | Start with |
|---|---|---|
| Data structure design | Which operations, how fast, and what must always stay true? | LRU cache |
| Object and API design | Which part is responsible for what? How do variations fit in? | Design principles, then file system and file search |
| Distributed systems | What happens with many machines, failures, retries, and simultaneous writes? | The steps below. Full case studies are coming. |
These connect, but one doesn't answer another. An in-memory file tree has no replication. A distributed cache can split data across machines well and still evict the wrong entry on one of them.
Interview questions and principles
- Design principles: SOLID: when each principle helps, what overusing it costs, and when to break it.
- Object design interview questions: classes, interfaces, patterns, and where the textbook answers break down.
- System design interview questions: scaling, caching, data, and reliability, with the same honesty about limits.
Pin down the requirements
List the main use cases, and say what's out of scope. For each operation, write who calls it, what it returns, what happens when it fails, and what must always stay true. Decide which data is the source of truth and which can be rebuilt. "Received" and "safely stored" are different promises.
For example, "store files" leaves a lot open: the largest file size, whether writes append or replace, whether renames are all-or-nothing, permissions, two writers at once, and what a reader sees right after a write.
Estimate only what changes a decision
Estimate traffic, the mix of reads and writes, sizes, how long data is kept, and hot spots, but only when the number will change your design. One million requests a day is about 12 per second on average. Peaks and popular keys can be far higher, and an average tells you nothing about bursts or slow requests.
Always write units. Turn requests per second into bytes per second, then into storage per day. Label your assumptions, then change them: does your design still hold?
Follow one request, then break something
Start with the smallest design that meets the requirements. Follow a write from validation to a confirmed save, and a read from the front door to the source of truth. Then break one thing: a lost response, a slow dependency, a duplicate request, an out-of-date copy, or a crashed process.
For each failure, ask what the caller knows and what has already happened. A timeout doesn't mean the operation failed. To retry safely, make the operation idempotent: doing it twice has the same effect as doing it once. Adding a queue changes when work finishes, and brings duplicates and backlogs with it.
Add a part only to fix a named problem
- A cache cuts repeated reads, but you must decide how stale its data may get and how to clear it.
- Partitioning spreads data across machines, but brings hot keys and rebalancing.
- Replication survives failures, but forces choices about coordination and consistency.
Compare at least two options, and say what would make you switch. "A database, a cache, and a queue" is a list of parts, not a design.
What each design lesson covers
The requirements, a simple first version, the pressure that forces a change, the design, a trace of each operation, what must always stay true, failure cases, tests, and exercises that change the requirements. The current lessons are the LRU cache and the in-memory file system.
Larger service designs will come as separate, fully reviewed lessons. For framing, the free ByteByteGo foreword stresses open-ended requirements, communication, and tradeoffs. It's useful context, not an answer key.