System Design Interview Guide
System Design Interview Guide
Topics Covered
Low-Latency Systems | High-Throughput Services | Concurrent Programming
Performance Profiling & Tuning | SQL/NoSQL Databases | Distributed Caching
Multi-Threading | Real-Time Programming
Page 1
Table of Contents
1. Low-Latency Systems3
2. High-Throughput Services7
3. Concurrent Programming11
4. Performance Profiling and Tuning16
5. SQL vs NoSQL Databases20
6. Distributed Caching25
7. Multi-Threading30
8. Real-Time Programming35
9. Reference Materials & Resources39
Page 2
1. Low-Latency Systems
A. Caching
Page 3
Caching stores frequently accessed data in fast-access memory (like RAM) so it does not need
to be fetched from slower storage or recomputed. This is the single most impactful latency
reduction technique.
In-Memory Caches: Redis and Memcached keep hot data in RAM, providing sub-millisecond
read times. Use cache-aside pattern (check cache first, fall back to DB, then populate cache) or
write-through (write to cache and DB simultaneously).
CDN Caching: Content Delivery Networks like CloudFront or Akamai cache static assets at
edge locations geographically close to users, dramatically reducing round-trip time for static
content.
Application-Level Caching: Use local in-process caches (like Guava Cache in Java or
lru_cache in Python) for data that changes infrequently and is read often.
@lru_cache(maxsize=1024)
def get_user_profile(user_id: str):
# This DB call only happens on cache miss
return [Link](f'SELECT * FROM users WHERE id = {user_id}')
B. Connection Pooling
Creating a new TCP connection or database connection for every request is expensive (TCP
handshake alone is ~1.5 round trips). Connection pooling pre-creates and reuses connections,
eliminating this overhead.
// Java: HikariCP Connection Pool Configuration
HikariConfig config = new HikariConfig();
[Link]("jdbc:postgresql://localhost:5432/mydb");
[Link](20);
[Link](5);
[Link](3000); // 3s max wait
HikariDataSource ds = new HikariDataSource(config);
Page 4
const user = await [Link]([Link]);
const orders = await [Link](`orders:${[Link]}`);
[Link]({ user, orders });
});
E. Protocol Optimization
• Use gRPC instead of REST for internal service communication (binary serialization is
5-10x faster than JSON).
• Use HTTP/2 or HTTP/3 (QUIC) for multiplexed connections, avoiding head-of-line
blocking.
• Use Protocol Buffers or FlatBuffers instead of JSON for serialization to reduce parsing
overhead.
Interview Questions
Page 5
1. Design a system that serves API responses in under 50ms at P99. What architecture choices
would you make?
2. How would you reduce the latency of a service that currently takes 500ms to respond?
3. Explain the latency implications of synchronous vs asynchronous processing.
4. How does data replication strategy affect read latency vs write latency?
5. You have a microservice that calls three downstream services sequentially. How do you
reduce overall latency?
6. What is tail latency and why does it matter at scale? How do you mitigate it?
7. Design a low-latency notification system for a social media platform.
Page 6
2. High-Throughput Services
A. Horizontal Scaling
Add more machines to distribute load. This is the most fundamental approach for throughput at
FAANG scale. Stateless services are easiest to scale horizontally because any instance can
serve any request.
# Kubernetes HPA: Auto-scale based on CPU/RPS
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: api-server
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: api-server
minReplicas: 3
maxReplicas: 50
Page 7
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
B. Batching
Instead of processing items one at a time, group them into batches. This amortizes per-request
overhead (network round trips, context switches, disk seeks) across many items.
// Java: Batch database inserts
PreparedStatement ps = [Link](
"INSERT INTO events(user_id, type, ts) VALUES(?,?,?)");
C. Load Balancing
Distribute incoming requests evenly across servers. Common strategies include Round Robin,
Least Connections, Consistent Hashing (for cache-friendly routing), and Weighted distribution
(send more traffic to powerful machines). Layer 4 (TCP) load balancing is faster but less flexible;
Layer 7 (HTTP) allows content-based routing.
producer = KafkaProducer(
bootstrap_servers=['kafka1:9092', 'kafka2:9092'],
batch_size=65536, # 64KB batch
Page 8
linger_ms=10, # Wait up to 10ms to fill batch
compression_type='lz4', # Compress batches
acks=1 # Leader ack only for throughput
)
F. Compression
Compress data in transit and at rest. Use gzip or Brotli for HTTP responses, Snappy or LZ4 for
internal communication (fast compression, lower ratio), and columnar formats like Parquet for
analytical workloads (10x or more compression for repetitive data).
Page 9
[Link] = now;
}
}
Interview Questions
1. Design a system that handles 1 million requests per second. Walk through your architecture.
2. How would you design a log aggregation pipeline processing 10TB of logs per day?
3. Explain how Kafka achieves high throughput. What are partitions and consumer groups?
4. How do you handle a sudden 10x traffic spike without dropping requests?
5. Design a URL shortener that handles 100K reads/sec and 1K writes/sec.
6. What is back pressure and how do you implement it in a distributed system?
7. Compare horizontal vs vertical scaling. When would you choose each?
Page 10
3. Concurrent Programming
Page 11
Instead of sharing memory, processes/actors communicate by sending immutable messages.
Each actor has its own state and processes messages sequentially. This eliminates
shared-state bugs by design. Erlang, Akka (Scala/Java), and Go channels use this model.
// Go: Channel-based message passing
func producer(ch chan<- int) {
for i := 0; i < 100; i++ {
ch <- i // Send message
}
close(ch)
}
Page 12
Primitive Purpose Use Case
Mutex / Lock Mutual exclusion; only one Protecting shared counters,
thread at a time maps
Semaphore Allow N concurrent accesses Connection pool with max size
Read-Write Lock Multiple readers OR one writer Config that is read often,
updated rarely
Condition Variable Wait until a condition is true Producer-consumer queue
Atomic Variables Lock-free single-variable Counters, flags, CAS operations
operations
Barrier Wait until N threads reach a Phased parallel computation
point
Race Conditions
Two threads access shared data simultaneously, and the result depends on timing. The classic
example is a check-then-act sequence: checking a condition and acting on it are not atomic, so
another thread can change the state between the check and the act.
// RACE CONDITION EXAMPLE (Java)
// Thread A and Thread B both execute this:
if (balance >= amount) { // CHECK
balance -= amount; // ACT
}
// If both threads check before either acts,
// balance can go negative!
Deadlocks
Two or more threads each hold a resource the other needs, and neither can proceed. The four
necessary conditions are: mutual exclusion, hold and wait, no preemption, and circular wait.
Break any one condition to prevent deadlocks.
// DEADLOCK EXAMPLE
// Thread 1: lock(A) -> lock(B)
Page 13
// Thread 2: lock(B) -> lock(A)
Interview Questions
1. What is the difference between concurrency and parallelism? Give real-world examples.
2. Implement a thread-safe bounded blocking queue.
3. How would you design a concurrent web crawler that respects rate limits?
4. Explain the dining philosophers problem and your solution.
5. What are the four conditions for deadlock? How do you prevent each?
6. Compare mutexes, semaphores, and monitors. When would you use each?
7. Design a concurrent LRU cache with O(1) operations.
8. How does Go's concurrency model differ from Java's? Which would you choose and why?
Page 14
4. Performance Profiling and Tuning
Amdahl's Law
If 80% of execution time is in function X, even making X infinitely fast only gives a 5x overall
speedup. Always optimize the biggest bottleneck first.
A. CPU Profiling
Identifies which functions consume the most CPU time. Sampling profilers (like Linux perf,
async-profiler for Java) periodically record the call stack, creating flame graphs. Instrumentation
profilers (like gprof) add timing code to every function call but have higher overhead.
# Linux perf: CPU flame graph generation
perf record -F 99 -g -p <PID> -- sleep 30
perf script | [Link] | [Link] > [Link]
B. Memory Profiling
Tracks heap allocations, identifies memory leaks, and finds objects that are not being garbage
collected. Tools include Valgrind (C/C++), JVisualVM / Eclipse MAT (Java), memory_profiler
(Python), and Chrome DevTools Heap Snapshots (JavaScript).
# Python: memory_profiler
from memory_profiler import profile
@profile
def process_data():
data = [i ** 2 for i in range(1_000_000)] # 8MB allocation
filtered = [x for x in data if x % 2 == 0] # Another 4MB
return sum(filtered)
Page 15
C. I/O Profiling
Identifies slow disk reads, network calls, and database queries. Tools include strace/ltrace
(system call tracing), tcpdump/Wireshark (network), and slow query logs (database). The most
common bottleneck in web services is I/O, not CPU.
D. Distributed Tracing
In microservices architectures, a single user request may traverse dozens of services.
Distributed tracing tools like Jaeger, Zipkin, and AWS X-Ray propagate trace IDs across service
boundaries, allowing you to see the full request path and identify which service is the bottleneck.
Page 16
JVM Tuning (for Java/Scala/Kotlin)
• Heap sizing: Set -Xms and -Xmx to the same value to avoid resize pauses.
• GC selection: Use G1GC for balanced latency/throughput; ZGC or Shenandoah for
ultra-low pause times.
• Thread pool sizing: For CPU-bound tasks, use N threads (N = number of cores). For
I/O-bound, use N * (1 + wait_time/compute_time).
Interview Questions
1. A production service's P99 latency spiked from 50ms to 2s. How do you diagnose and fix it?
2. Explain what a flame graph shows and how you would use it to optimize a service.
3. How do you identify and fix N+1 query problems?
4. What are the different garbage collection algorithms and their trade-offs?
5. How would you optimize a service that is CPU-bound vs one that is I/O-bound?
6. Explain Amdahl's Law and its implications for performance optimization.
7. How do you load test a system? What tools would you use and what metrics would you track?
Page 17
5. SQL vs NoSQL Databases
COMMIT;
Page 18
Document Store MongoDB, Couchbase, Semi-structured data, CMS,
Firestore catalogs
Wide-Column Store Cassandra, HBase, ScyllaDB Time-series, IoT, write-heavy
workloads
Graph Database Neo4j, Amazon Neptune, Social networks,
ArangoDB recommendations, fraud
detection
Page 19
Indexes are the single most important performance feature in any database. A B-Tree index
turns an O(n) table scan into O(log n) lookup. However, each index adds write overhead (the
index must be updated on every insert/update/delete) and storage cost.
Interview Questions
1. You are designing a social media platform. Would you use SQL, NoSQL, or both? Justify your
choice.
2. Explain the CAP theorem with concrete examples. What does your system sacrifice?
3. How does database sharding work? What are the challenges of resharding?
4. Design the database schema for an e-commerce platform (users, products, orders, reviews).
5. What are the isolation levels in SQL? Explain phantom reads and how to prevent them.
6. When would you choose Cassandra over PostgreSQL? What about DynamoDB?
7. How do indexes work internally? When would adding an index actually hurt performance?
8. Explain eventual consistency. How do you handle it in application code?
Page 20
Page 21
6. Distributed Caching
B. Distributed Cache
Dedicated caching servers shared by all application instances. Redis and Memcached are the
dominant choices. Adds a network hop (~0.5ms) but provides shared state, larger capacity, and
persistence options.
Page 22
Cache-Aside (Lazy Loading)
The application checks the cache first. On cache miss, it fetches from the database, stores in
cache, then returns. This is the most common pattern. Pros: Only requested data is cached (no
wasted space). Cons: Cache miss penalty (three round trips: cache check, DB fetch, cache
write). Data can become stale.
# Python: Cache-Aside Pattern
def get_user(user_id):
# 1. Check cache
cached = [Link](f'user:{user_id}')
if cached:
return [Link](cached)
Write-Through
Every write goes to both the cache and database. Ensures cache is always consistent with the
database. Pros: Cache is never stale. Cons: Higher write latency (two writes per operation). May
cache data that is never read.
Write-Behind (Write-Back)
Writes go to cache first, then asynchronously to database in batches. Pros: Lowest write
latency, batching reduces DB load. Cons: Risk of data loss if cache fails before flush,
complexity. Used in systems where write performance is critical and some data loss is tolerable.
Read-Through
The cache itself is responsible for loading data from the database on a miss (vs the application
doing it in cache-aside). Simplifies application code but requires cache infrastructure to support
it.
Page 23
Cache Stampede Prevention
When a popular cache key expires, hundreds of concurrent requests may all miss cache and hit
the database simultaneously. Solutions include: lock/mutex (only one thread fetches, others
wait), early/probabilistic refresh (refresh before expiration), and request coalescing (deduplicate
identical in-flight requests).
Interview Questions
1. Design a distributed cache for a social media feed. How do you handle cache invalidation?
2. Explain the differences between cache-aside, write-through, and write-behind patterns. When
would you use each?
3. How does consistent hashing work? What are virtual nodes and why are they needed?
4. How would you prevent a cache stampede on a popular key?
5. Redis vs Memcached: when would you choose each?
6. Design a multi-level caching architecture (L1 local, L2 distributed, L3 CDN).
7. How do you handle cache consistency in a microservices architecture where multiple services
modify the same data?
8. What is cache warming and when would you use it?
Page 24
7. Multi-Threading
7.1 Fundamentals
A thread is the smallest unit of execution within a process. Multiple threads within a process
share the same memory space (heap, global variables) but each has its own stack, program
counter, and registers. Multi-threading allows a program to perform multiple operations
concurrently, utilizing multiple CPU cores for parallel execution.
// Submit tasks
Page 25
Future<Result> future = [Link](() -> {
return processRequest(request);
});
Result result = [Link](5, [Link]); // With timeout
// Lock-free increment
int oldVal, newVal;
do {
oldVal = [Link]();
newVal = oldVal + 1;
} while ();
Page 26
7.7 Common Interview Questions
Interview Questions
1. Implement a producer-consumer pattern using threads, locks, and condition variables.
2. What is a thread pool and how do you size it? What happens if the task queue overflows?
3. Explain the difference between volatile and synchronized in Java.
4. What is a ConcurrentHashMap and how does it achieve thread safety without locking the
whole map?
5. Implement a read-write lock from scratch.
6. What is the ForkJoinPool and when would you use it over a regular thread pool?
7. How would you debug a thread leak in a production Java application?
8. Explain false sharing and how it impacts multi-threaded performance on multi-core CPUs.
Page 27
8. Real-Time Programming
WebSockets
WebSockets provide full-duplex communication over a single TCP connection. After an HTTP
handshake upgrades the connection, both client and server can send messages at any time.
This eliminates the overhead of repeated HTTP requests and enables true push-based
communication.
// [Link]: WebSocket server with [Link]
const io = require('[Link]')(httpServer, {
cors: { origin: '*' }
});
Page 28
// Broadcast message to room
[Link]('message', (data) => {
[Link]([Link]).emit('message', {
sender: [Link],
text: [Link],
timestamp: [Link]()
});
});
});
// Subscribe to updates
const unsubscribe = [Link](sendEvent);
[Link]('close', () => unsubscribe());
});
Long Polling
The client sends an HTTP request and the server holds it open until new data is available (or a
timeout). Once the response is sent, the client immediately sends a new request. This is the
simplest approach and works through all proxies/firewalls but has higher overhead than
WebSockets due to repeated connection setup.
Page 29
Pub/Sub for Real-Time Fanout
When a user posts a message, it must be delivered to potentially millions of followers. Redis
Pub/Sub, Kafka, or a dedicated message broker fans out the message to all connected
WebSocket servers, which then push to their connected clients. This decouples message
production from delivery.
# Architecture: Real-time notification fanout
#
# User posts message
# -> API Server publishes to Kafka topic 'notifications'
# -> WebSocket Server 1 (consumer) pushes to its connected clients
# -> WebSocket Server 2 (consumer) pushes to its connected clients
# -> WebSocket Server N (consumer) pushes to its connected clients
Page 30
Interview Questions
1. Design a real-time chat application supporting 10 million concurrent users.
2. Compare WebSockets, SSE, and Long Polling. When would you use each?
3. How would you design a real-time collaborative document editor like Google Docs?
4. Design a live sports scoreboard that updates all connected clients within 1 second.
5. How do you handle message ordering in a distributed real-time system?
6. Design a real-time notification system for a social media platform.
7. What is the Operational Transformation (OT) algorithm? How does it enable real-time
collaboration?
8. How would you scale WebSocket servers horizontally?
Page 31
9. Reference Materials & Resources
2. ByteByteGo by Alex Xu
Alex Xu (author of the System Design Interview books) runs the ByteByteGo channel with
animated, visual explanations of system design concepts. Excellent for understanding
distributed caching, CAP theorem, and high-throughput architectures.
Channel: [Link]/@ByteByteGo
Page 32
9.3 Practice Platforms
• LeetCode (Concurrency section): Practice multi-threading problems like Print in Order,
Building H2O, Dining Philosophers.
• [Link] - Grokking the System Design Interview: Structured course covering all
major system design patterns.
• System Design Primer (GitHub): Free, comprehensive guide with diagrams and code
examples. [Link]/donnemartin/system-design-primer
Page 33