Design
System design without hand-waving
The round has no correct answer, which is exactly why it is possible to fail it badly. What is scored is the reasoning, and reasoning has to be made audible.
The most common way to lose this round is to start drawing. A prompt arrives — design a URL shortener, a news feed, a rate limiter — and the reflex is to put boxes on the board because boxes look like progress. Ten minutes later you have an architecture for a system whose size, read/write mix and consistency requirements nobody has established, and every subsequent decision is untethered.
The candidates who did well in the rounds I sat in on spent the first ten minutes not designing anything.
The first ten minutes
Three things, in order, before any component is named:
- Functional scope. What must it do, and — more usefully — what are you explicitly not building? "I'll assume we don't need analytics or an admin console; tell me if that's wrong" is a strong opening move. It bounds the problem and shows you know these systems have more surface than one hour holds.
- Non-functional constraints. How many users, what read-to-write ratio, how much data retained, how fresh must reads be, what latency is acceptable, what happens if the thing is down for a minute. These decide the design; without them any architecture is as defensible as any other.
- Arithmetic. Turn the constraints into numbers out loud. This is where the interviewer learns whether you have operated a system or only read about one.
Ask for the constraints; do not wait to be given them. Most interviewers deliberately under-specify the prompt and are scoring whether you notice. If they decline to answer, state an assumption and move — "I'll assume ten million daily actives and a hundred-to-one read skew" is a perfectly good substitute for being told.
Numbers worth having memorised
Not for their own sake — for doing estimates out loud without stalling. Order of magnitude is all that is wanted.
| Quantity | Rough value |
|---|---|
| Seconds in a day | ~86,400 — call it 105 |
| 1M writes/day | ~12 per second |
| 1B rows × 100 bytes | ~100 GB |
| Memory reference | ~100 ns |
| SSD random read | ~100 µs |
| Disk seek (spinning) | ~10 ms |
| Round trip, same datacentre | ~0.5 ms |
| Round trip, cross-continent | ~100–150 ms |
| Commodity server | tens of thousands of QPS if the work is small |
The useful trick is that peak traffic is typically two to three times the daily average, and that a system doing fewer than a few thousand writes per second usually does not need any of the exotic machinery candidates reach for. Saying so is a strength, not a cop-out.
The component vocabulary
Most designs are assembled from a small kit. Knowing what each piece buys you — and what it costs — matters far more than knowing product names.
Spreads traffic and removes single instances from the picture. Cheap, uncontroversial, and rarely the interesting part of the answer.
Stateless by preference, because stateless scales by addition. If you put state here, say why, and say what happens when the instance dies.
The correct default. Transactions, joins, constraints, and decades of operational knowledge. Justify moving away from it; do not justify choosing it.
For high-volume access by a known key where you do not need joins. Buys throughput and horizontal scale, costs you query flexibility and often consistency.
Absorbs read skew. The design question is never "add a cache" — it is what gets invalidated, by whom, and what a stale read does to the user.
Decouples a slow or bursty consumer from a fast producer. Turns a latency problem into a backlog problem, which is usually the better problem, and introduces duplicate delivery, which you should mention before you are asked.
For blobs. Keep bytes out of the database and keep the metadata in it. Serve through a CDN if the read volume justifies it.
For text queries a relational store handles badly. A second copy of the data, therefore a synchronisation problem, therefore a staleness window to acknowledge.
Working through the middle of the hour
Once the constraints are on the board, a workable order is: the data model first, then the read path, then the write path, then what breaks at ten times the load.
Doing the data model early is underrated. Sketching the two or three entities and their access patterns forces the question that decides most of the architecture — what you look things up by. A design that starts from access patterns tends to justify itself; a design that starts from boxes tends to need retrofitting once someone asks how a particular query is served.
The read path is usually where the volume is, and where caching, denormalisation and fan-out choices belong. The write path is where correctness lives: idempotency, ordering, what a retry does, what a partial failure leaves behind. Interviewers probe the write path when they want to find your level.
Trade-offs, said properly
Every real choice costs something, and naming the cost unprompted is the clearest available evidence of experience. A few worth being fluent in:
- Fan-out on write versus on read. Precomputing a feed makes reads trivial and makes a celebrity's post an expensive event. Computing at read time inverts both. The usual answer is a hybrid, and saying so is fine as long as you say where the boundary is.
- Strong versus eventual consistency. Not a global setting — a per-operation decision. A balance transfer and a like count do not need the same guarantee, and noticing that is worth more than reciting a theorem.
- Sharding key. The choice that is hardest to reverse later. Say what becomes expensive under the key you pick, and what a hot shard would look like.
- Synchronous versus queued. Moving work off the request path improves latency and gives up the ability to tell the user it worked.
Traps
- Naming products instead of properties. "We'll use Kafka" says less than "we need an ordered, replayable log with multiple independent consumers."
- Scaling a problem you do not have. Sharding a database holding four gigabytes reads as pattern-matching rather than thinking.
- Silent boxes. An arrow between two components is a protocol, a failure mode and a latency budget. Leaving all three unspoken is the most common thin answer.
- Refusing to commit. Presenting three options and picking none is a non-answer. Choose, justify, and name what would change your mind.
- Ignoring operations. How you would know it is broken, what you would alert on, and how you would deploy a change are cheap points that most candidates leave on the table.