System Design Interview Guide: A 5-Step Roadmap
System design interviews are not primarily tests of how many technologies you can name. They test whether you can turn unclear requirements into a sensible architecture, explain your decisions and recognise the trade-offs behind them.
This guide organises the preparation into five steps:
flowchart LR
A["1. Fundamentals"] --> B["2. Scalability"]
B --> C["3. Architecture patterns"]
C --> D["4. Data management"]
D --> E["5. Hands-on practice"]
The goal is not to memorise one perfect architecture. It is to build a repeatable way of thinking.
Step 1: Fundamentals
Before discussing distributed databases or event-driven systems, you need a strong understanding of how requests, APIs, storage and caching work.
Networking
HTTP/1.1 versus HTTP/2
HTTP/1.1 normally processes one outstanding request at a time per connection unless pipelining is used. Browsers often open several connections to compensate, but this adds overhead.
HTTP/2 uses binary framing and multiplexes many request and response streams over a single TCP connection. This makes it more efficient when a page or application needs many small resources.
TCP and connection setup
The TCP handshake uses three messages:
Client → SYN
Server → SYN-ACK
Client → ACK
This adds a network round trip before application data moves. At global scale, connection establishment becomes a meaningful part of latency. QUIC and HTTP/3 reduce some of this cost by combining transport security and connection establishment more efficiently.
DNS
DNS translates a domain name into an IP address. Results are cached at several levels:
- browser;
- operating system;
- local or corporate resolver;
- recursive DNS resolver.
Caching avoids repeating the complete lookup for every request, but it also means DNS changes take time to propagate according to the configured TTL.
Layer 4 versus Layer 7 load balancing
| Layer | Uses | Strength | Trade-off |
|---|---|---|---|
| L4 | IP addresses, ports and transport information | Fast and protocol-agnostic | Cannot route using HTTP content |
| L7 | Hostnames, paths, headers and cookies | Intelligent application routing | More processing and configuration |
An L7 load balancer can route /images and /api to different services, while an L4 load balancer forwards connections without understanding HTTP semantics.
API design
REST versus GraphQL
| REST | GraphQL |
|---|---|
| Models resources through endpoints | Models data through a query schema |
| Simple HTTP caching | More complicated caching |
| Can over-fetch or under-fetch | Clients request specific fields |
| Straightforward operational model | Flexible for clients with different data needs |
| Many endpoints may be required | One endpoint may serve many query shapes |
GraphQL flexibility also creates additional concerns around query cost, authorisation and abuse prevention.
Rate limiting
Rate limiting protects a service from overload and prevents one client from consuming all available capacity.
Token bucket accumulates tokens at a steady rate. A request consumes a token, allowing short bursts while enforcing a long-term average.
Sliding window counts requests within a continuously moving time interval. It provides smoother enforcement but typically requires more state.
Sessions versus JWTs
| Server-side session | JWT |
|---|---|
| State is stored by the server | Claims are carried in the token |
| Easy to revoke centrally | Difficult to revoke before expiry |
| Requires shared session storage when scaling | Easy for multiple service instances to verify |
| Session ID is normally stored in a cookie | Token must be protected from theft and misuse |
JWT revocation is difficult precisely because a service can validate the token without consulting central state. Common mitigations include short expiries, refresh-token rotation and revocation lists.
Database basics
SQL versus NoSQL
Relational databases provide schemas, joins, constraints and transactions. NoSQL systems often provide flexible models and easier horizontal distribution for specific access patterns, but usually trade away some relational capabilities.
Neither category is automatically more scalable. The right choice depends on queries, consistency requirements, data shape and operating scale.
B-tree versus hash indexes
| B-tree index | Hash index |
|---|---|
| Maintains sorted keys | Maps keys through a hash function |
| Supports exact matches | Excellent for exact matches |
| Supports range and ordered queries | Does not naturally support range scans |
| Useful for dates and sortable values | Useful for equality-based lookups |
Range versus hash partitioning
Range partitioning groups nearby key values together. It supports efficient range queries but may create hot partitions when activity concentrates in one range.
Hash partitioning spreads keys more evenly, improving load distribution while reducing range-query locality.
Caching
| Pattern | How it works | Main trade-off |
|---|---|---|
| Cache-aside | Application reads the cache and loads from the database on a miss | Simple, but cached data can become stale |
| Write-through | Writes update the cache and durable store together | Consistent reads, slower writes |
| Write-behind | Writes enter the cache and reach storage asynchronously | Fast writes, risk during failures |
A cache stampede occurs when a popular entry expires and many requests simultaneously query the database. Mitigations include:
- per-key locks;
- request coalescing;
- randomised or staggered expiry;
- refreshing popular values before expiry;
- serving stale values briefly while refreshing.
A CDN caches content near users at edge locations. It is particularly effective for static files, images and video. An application or origin cache normally sits closer to the service and can cache dynamic or computed results.
Step 2: Scalability and Performance
Vertical versus horizontal scaling
| Vertical scaling | Horizontal scaling |
|---|---|
| Add CPU, memory or storage to one machine | Add more machines |
| Operationally simple | Scales beyond one machine’s limit |
| Eventually reaches a hardware ceiling | Requires load distribution and coordination |
| Can remain a single point of failure | Introduces distributed-system failures |
Horizontal scaling is not free. Function calls become network calls, replicas can disagree and failures may be partial rather than complete.
Load-balancing algorithms
Round robin cycles evenly through servers. It is simple, but assumes requests and servers have similar cost and performance.
Least connections sends a request to the server with the fewest active connections. It adapts better to unequal request duration, although connection count is not always a perfect measure of actual load.
Consistent hashing places servers and keys on a hash ring. Adding or removing a server moves only part of the keyspace. This is useful for distributed caches because it preserves cache locality during membership changes.
Replication
Leader-follower
Writes go to one leader and are copied to followers. Followers can serve reads, but replication lag means a user may not immediately observe a completed write when reading from a follower.
Multi-leader
Several nodes accept writes. This is useful across regions or disconnected environments, but simultaneous changes can conflict and require a resolution policy.
Quorums
For N replicas, a write may require acknowledgements from W replicas and a read may consult R replicas. Increasing the quorum improves consistency confidence but adds latency and can reduce availability during failures.
Sharding
| Strategy | Advantage | Risk |
|---|---|---|
| Range sharding | Efficient range queries | Hot ranges and uneven growth |
| Hash sharding | Even distribution | Poor range-query locality |
| Directory-based sharding | Flexible placement | Extra lookup and directory dependency |
Resharding is the operational challenge of redistributing data when shards are added, removed or become unbalanced. Consistent hashing and virtual nodes reduce movement, but migration, dual writes and cutover still require careful design.
Asynchronous processing
Kafka versus RabbitMQ
| Kafka | RabbitMQ |
|---|---|
| Distributed append-only log | Traditional message broker |
| High-throughput streaming | Flexible message routing |
| Consumers track positions and can replay | Per-message acknowledgement and queues |
| Strong fit for event streams and analytics | Strong fit for work queues and routing patterns |
This is not an absolute rule. The choice depends on retention, replay, ordering, routing and operating model.
Delivery guarantees
At-least-once delivery means a message should not be lost, but it may be processed more than once. Consumers must therefore be idempotent or deduplicate using a stable event ID.
Exactly-once delivery is much harder to guarantee end to end because messaging, application logic and database writes cross different boundaries. Practical designs usually combine broker guarantees with transactional writes, idempotency and deduplication.
Backpressure
Backpressure is how a system prevents fast producers from overwhelming slower consumers. Possible responses include:
- slowing or rejecting producers;
- limiting concurrency;
- pausing consumption;
- buffering within bounded queues;
- shedding low-priority work;
- scaling consumers when additional parallelism is available.
Without backpressure, queues grow without limit, latency rises and the system eventually exhausts resources.
Step 3: Architecture Patterns
Monolith versus microservices
| Monolith | Microservices |
|---|---|
| One deployable unit | Independently deployable services |
| Easier local development and testing | Independent ownership and scaling |
| Simple transactions and calls | Network boundaries and distributed data |
| Deployment coupling increases with size | Operational complexity increases with service count |
The real trade-off is often deployment coupling versus operational complexity. A well-designed monolith can scale effectively, while poorly bounded microservices can make every change harder.
Event-driven versus request-response
Request-response is synchronous. The caller waits for the result, which is easy to understand but couples its latency and availability to the downstream service.
Event-driven interaction is asynchronous. Producers publish facts and consumers react later. This decouples services in time, but introduces eventual consistency and more complicated failure diagnosis.
Eventual consistency is acceptable when the business can tolerate temporary staleness, such as a delayed social-media counter. It may be unacceptable when checking a bank balance immediately before a withdrawal.
CQRS and event sourcing
CQRS separates the command model used for validating and persisting changes from the query model used for reads. It is valuable when reads and writes differ substantially in shape or scale.
Event sourcing stores state changes as immutable events instead of overwriting the current record. Current state is reconstructed by replaying events.
Benefits include auditability, historical reconstruction and replay. Costs include event evolution, storage, debugging complexity and projection management.
Snapshots periodically store derived state so a service does not need to replay the complete history on every recovery.
Fault tolerance
Circuit breaker
A circuit breaker stops repeatedly calling an unhealthy dependency. It fails fast while the circuit is open and periodically probes whether the dependency has recovered.
Retries with backoff and jitter
Backoff increases the delay between attempts. Jitter adds randomness so many clients do not retry at exactly the same moment and overwhelm a recovering service.
Retries should be used only for transient and safe-to-repeat operations. Retrying an invalid request or a non-idempotent operation can make the problem worse.
Bulkheads
Bulkheads isolate resources such as thread pools, queues and connection pools per dependency. A failure in one integration then cannot consume every resource needed by the rest of the application.
Step 4: Data Management
CAP theorem
During a network partition, a distributed system must choose between:
- Consistency: every read observes the required latest state;
- Availability: every request receives a non-error response, even when that response may be stale.
Partition tolerance is not normally optional in a distributed system. CAP is therefore about behaviour during a partition, not about permanently labelling an entire product as only “CP” or “AP”. Different operations may choose different trade-offs.
PACELC
PACELC extends the discussion:
If there is a Partition: choose Availability or Consistency.
Else: choose Latency or Consistency.
Even during healthy operation, synchronously coordinating replicas improves consistency at the cost of latency.
Consistency models
| Model | Guarantee |
|---|---|
| Strong consistency | Reads observe the latest successful write according to the system’s contract |
| Eventual consistency | Replicas converge, but a read may temporarily be stale |
| Causal consistency | Causally related operations are observed in order |
| Read-your-writes | A client observes its own completed writes |
Choose the model from the business invariant. A product catalogue and a payment balance do not necessarily need the same guarantee.
Choosing a storage system
A relational database with suitable indexes can handle substantial scale while preserving joins, constraints and transactions. Most systems should not abandon relational modelling merely because NoSQL sounds more scalable.
NoSQL becomes compelling when the workload has characteristics such as:
- extremely high write throughput;
- flexible or rapidly changing document structures;
- access patterns that do not benefit from joins;
- very wide time-series data;
- simple key-based access distributed across many partitions.
The decision should begin with queries and invariants, not a technology label.
Query optimisation
An EXPLAIN plan shows how the database intends to execute a query, including scans, index access, join order and row estimates. It should be the first tool used when investigating a slow query.
A covering index contains every column required by a query, allowing the database to answer from the index without reading the full table row.
The N+1 query problem occurs when an application fetches a list and then runs one additional query for every item. It can often be fixed with a join, batch fetch or one IN (...) query.
Step 5: Hands-On Practice
Design systems end to end
Do not stop at a whiteboard diagram. Define enough detail to expose hidden assumptions:
- functional and non-functional requirements;
- traffic and storage estimates;
- API request and response shapes;
- status codes and error contracts;
- database schema and indexes;
- partition keys;
- pagination;
- concurrency behaviour;
- consistency boundaries;
- failure and recovery paths;
- monitoring and capacity signals.
This is where vague designs become testable designs.
Document trade-offs explicitly
For each major decision, state:
We chose X because we are optimising for Y.
The cost is Z.
We would reconsider this decision if condition A changed.
Interviewers are usually more interested in whether you understand the consequences than whether you chose their favourite database.
Practise out loud
Speaking through a design under time pressure is different from silently writing one. Practise with a peer, mentor or recording. Train yourself to:
- clarify the problem;
- state assumptions;
- estimate scale;
- draw the high-level architecture;
- deepen the critical path;
- identify bottlenecks and failures;
- explain alternatives and trade-offs.
Debrief each mock interview
After every mock, identify the most important question you should have asked earlier. Common omissions include:
- expected traffic;
- read-to-write ratio;
- latency target;
- data-retention period;
- regional distribution;
- consistency requirement;
- largest expected object;
- acceptable data loss;
- recovery-time objective.
Designing for the wrong constraints is one of the most common system-design mistakes.
A Repeatable Interview Framework
Use this sequence during the interview:
1. Clarify requirements
Separate must-have functionality from optional scope. Ask about users, scale, latency, consistency, security and availability.
2. Estimate scale
Calculate rough requests per second, storage growth, bandwidth and read/write ratios. Approximate numbers are enough if the assumptions are explicit.
3. Define APIs and data
Identify core entities, API operations, schemas, indexes and partition keys.
4. Draw the high-level flow
Show clients, gateways, services, storage, caches and asynchronous components. Keep the first diagram simple.
5. Deep-dive into the hardest path
Choose the most important risk: fan-out, ordering, consistency, hot keys, search, delivery guarantees or multi-region failover.
6. Cover failures and operations
Explain retries, timeouts, idempotency, replication, monitoring, recovery and deployment.
7. Summarise the trade-offs
End by stating what the design optimises for, what it sacrifices and what would change at ten times the scale.
Systems Worth Designing
Practise these systems end to end:
- URL shortener;
- distributed rate limiter;
- news feed or timeline;
- chat application;
- distributed cache;
- file-storage or Dropbox-like platform;
- ride-sharing dispatch;
- notification system.
For each exercise, avoid copying a standard diagram. Change one important constraint and observe how the design changes. For example:
- require global active-active writes;
- require strict ordering per user;
- allow no data loss;
- support very large files;
- add a 100 ms latency target;
- require data residency by country.
Recommended YouTube Resources
Final Advice
A strong system design answer is not the biggest possible architecture. It is an architecture that matches the stated requirements and whose weaknesses you understand.
Start simple. Make assumptions explicit. Deepen the parts that carry the most risk. Explain what fails, how the system recovers and why each major trade-off is acceptable.
That is the skill the interview is really testing.
