System Design Interviews Explained: What Companies Actually Expect
A system design interview evaluates how an engineer turns an ambiguous product problem into a practical architecture. The goal is rarely to discover one perfect design. Interviewers are more interested in how requirements are clarified, assumptions are made, scale is estimated, components are selected, bottlenecks are identified, and engineering trade-offs are explained.
Strong candidates do not immediately draw databases, queues, caches, and microservices. They first establish what needs to be built, what scale it must support, and which system properties matter most. Architecture follows from those constraints.
Table of Contents
- What System Design Interviews Evaluate
- Start with Requirements and Constraints
- Estimate Scale Before Designing
- Build the High-Level Design
- Deep Dive into Bottlenecks and Trade-Offs
- Production Design Example
- Common Mistakes
- Interview Checklist
- Conclusion
- Key Takeaway
- More Articles to Read
What System Design Interviews Evaluate
A typical system design question sounds deceptively simple:
- Design a URL shortener.
- Design a chat application.
- Design a notification system.
- Design a file storage service.
Each problem can produce dozens of reasonable architectures. The interviewer is therefore not primarily testing whether a particular diagram matches a memorized solution.
The interview usually evaluates several engineering abilities at the same time:
| Area | What It Demonstrates |
|---|---|
| Requirements | Ability to turn an ambiguous problem into concrete system behavior |
| Scale estimation | Understanding of traffic, storage, bandwidth, and capacity |
| Architecture | Ability to divide the system into appropriate components |
| Data modeling | Understanding of access patterns, indexes, partitioning, and persistence |
| Scalability | Ability to identify bottlenecks and scale important components |
| Reliability | Understanding of failures, redundancy, retries, and recovery |
| Trade-offs | Ability to explain why one approach is preferable under given constraints |
| Communication | Ability to explain and evolve the design collaboratively |
Seniority changes the expected depth. A more experienced engineer is generally expected to recognize second-order problems: hotspots, failure domains, operational complexity, consistency implications, deployment behavior, and what happens when assumptions change.
A useful mental model is:
Requirements
|
v
Scale and Constraints
|
v
High-Level Architecture
|
v
Data and APIs
|
v
Bottlenecks
|
v
Reliability and Scaling
|
v
Trade-Offs
The interview should evolve through this sequence rather than starting with the final architecture.
Start with Requirements and Constraints
One of the most important system design skills is recognizing that the initial question is incomplete.
Consider:
Design a chat application.
This does not specify enough information to design the system. Important questions include:
- Is the system one-to-one chat, group chat, or both?
- Are messages persisted permanently?
- Are read receipts required?
- Is message ordering important?
- Should offline users receive notifications?
- Are attachments supported?
- How many users and concurrent connections are expected?
The purpose is not to spend half the interview collecting every possible requirement. The objective is to establish the few requirements that materially change the architecture.
Requirements can be separated into functional and non-functional categories.
| Functional Requirements | Non-Functional Requirements |
|---|---|
| Send a message | Low delivery latency |
| Receive messages | High availability |
| Load conversation history | Horizontal scalability |
| Support group conversations | Durable message storage |
| Send push notifications | Predictable behavior during failures |
Non-functional requirements often influence architecture more strongly than feature lists.
For example, a notification system requiring eventual delivery within several minutes can use a different architecture from a trading notification system requiring extremely low latency.
The candidate should also establish what is out of scope. Narrowing the problem allows the interview to spend more time on meaningful engineering decisions.
Estimate Scale Before Designing
Architecture should be proportional to the workload. A system serving 50 requests per second does not require the same design as one serving 500,000 requests per second.
Useful estimates commonly include:
- daily active users;
- requests per second;
- peak requests per second;
- read-to-write ratio;
- storage growth;
- network bandwidth;
- number of persistent connections.
Suppose a service has 50 million daily active users and each user performs 20 operations per day:
50,000,000 users × 20 operations
= 1,000,000,000 operations/day
Average requests per second:
1,000,000,000 / 86,400
≈ 11,600 requests/second
Average traffic is not enough. If peak traffic is approximately three times the average:
Peak traffic ≈ 35,000 requests/second
This immediately influences decisions about horizontal scaling, database throughput, caching, partitioning, and asynchronous processing.
Storage can be estimated similarly. If the system stores 100 million objects per day with an average object size of 2 KB:
100,000,000 × 2 KB
≈ 200 GB/day
≈ 73 TB/year
The estimates do not need false precision. Their purpose is to establish the order of magnitude and expose which parts of the architecture deserve attention.
Detailed techniques for request, storage, bandwidth, and capacity calculations are covered in Estimating Scale and Capacity Planning.
Build the High-Level Design
Once requirements and scale are understood, the next step is a simple architecture that satisfies the primary request flow.
A common starting point is:
Clients
|
v
Load Balancer
|
v
Application Service
|
v
Database
This is intentionally simple. Additional components should appear because the requirements justify them.
For example, if reads dominate writes and frequently requested data can tolerate caching:
Clients
|
v
Load Balancer
|
v
Application
/ \
v v
Cache Database
If expensive work does not need to complete synchronously:
Client
|
v
API Service
|
+------> Database
|
+------> Message Queue
|
v
Workers
The strongest explanation is not:
"A message queue is needed because scalable architectures use queues."
It is:
"The API does not need to wait for this work. Moving it to a durable queue reduces request latency, absorbs traffic spikes, and allows worker capacity to scale independently."
Every major component should solve an identifiable problem.
At this stage, the candidate should usually describe the primary request path from beginning to end:
1. Client sends request
2. Load balancer selects application instance
3. Application validates request
4. Application reads/writes required data
5. Asynchronous work is queued when necessary
6. Response returns to client
This validates that the components actually form a working system rather than merely an architecture diagram.
Deep Dive into Bottlenecks and Trade-Offs
After establishing the high-level architecture, the interview normally becomes more interesting. The interviewer may change a constraint or ask how a particular component behaves at scale.
Typical questions include:
- What happens when the database becomes the bottleneck?
- How are records partitioned?
- What happens if a worker processes the same message twice?
- How does the system survive an availability-zone failure?
- What happens when the cache becomes unavailable?
- How are hot partitions handled?
- What consistency guarantees are required?
- How would the design change at ten times the traffic?
This is where trade-off reasoning matters.
Consider database replication:
Primary
/ \
v v
Replica 1 Replica 2
Read replicas can increase read capacity, but asynchronous replication can introduce stale reads. Whether that is acceptable depends on the product requirement.
Similarly, adding a cache reduces database load:
Request
|
v
Cache
|
+--> HIT --> return
|
+--> MISS --> Database
But the design now needs to consider invalidation, stale data, cache failure, hot keys, and cache stampedes.
A good interview discussion therefore follows this pattern:
Requirement
|
v
Design Decision
|
v
Benefit
|
v
Trade-Off
|
v
Failure Behavior
For example:
Decision: partition messages by conversation ID.
Benefit: messages from the same conversation can remain on the same partition, simplifying ordering.
Trade-off: unusually active conversations can create hot partitions.
Failure behavior: partition ownership and message processing must recover safely when consumers fail.
This reasoning demonstrates more engineering maturity than simply naming technologies.
Production Design Example
Consider a shortened interview exercise: design a service that accepts jobs through an API and processes them asynchronously.
The first step is establishing requirements:
- clients submit jobs through an HTTP API;
- job processing can take several seconds;
- clients should not wait for processing to finish;
- jobs should survive worker failures;
- clients can query job status;
- duplicate processing is possible but should not corrupt results.
Assume the service receives 10 million jobs per day with a five-times peak factor:
10,000,000 / 86,400
≈ 116 jobs/second average
Peak:
116 × 5
≈ 580 jobs/second
A reasonable first architecture is:
Clients
|
v
Load Balancer
|
v
+-------------+
| API Service |
+-------------+
/ \
/ \
v v
Job Database Message Queue
|
+------+------+
| |
v v
Worker 1 Worker 2
\ /
\ /
v v
Job Database
The API creates a job record and publishes work to a queue. The client receives a job identifier immediately:
POST /jobs
Response:
{
"job_id": "job_84219",
"status": "queued"
}
The request path is short:
Client
|
v
API
|
+--> create job
|
+--> enqueue work
|
v
Return job_id
Workers consume jobs independently:
def process_job(message):
job_id = message["job_id"]
job = repository.get(job_id)
if job.status == "completed":
return
repository.mark_processing(job_id)
result = execute_job(job)
repository.complete(job_id, result)
This example immediately creates several useful interview discussions.
What if the queue is temporarily unavailable?
Writing the database record and publishing the message as two unrelated operations can leave a job stored but never queued. A transactional outbox or another reliable publication mechanism may be required when this failure mode cannot be tolerated.
What if the worker crashes after performing the operation but before acknowledging the message?
The queue may deliver the message again. Processing should therefore be idempotent where possible.
What if traffic suddenly increases to 5,000 jobs per second?
The queue absorbs temporary differences between producer and consumer throughput, while worker capacity can scale independently.
What if jobs become much slower?
Queue depth and oldest-message age increase even if API latency remains healthy. Monitoring only the synchronous API would miss the problem.
Useful production metrics therefore include:
- API request rate and latency;
- queue depth;
- oldest queued job age;
- worker processing rate;
- job processing latency;
- retry rate;
- failed jobs;
- database latency and saturation.
The important interview behavior is how the architecture developed. It started with requirements and a simple request flow. Reliability, idempotency, scaling, and observability were then added because specific failure modes justified them.
Common Mistakes
| Mistake | Why It Hurts the Design | Better Approach |
|---|---|---|
| Starting architecture immediately | The design is built before the actual requirements are known. | Clarify scope, scale, and critical system properties first. |
| Memorizing one architecture per problem | The solution becomes difficult to adapt when assumptions change. | Derive components from requirements and access patterns. |
| Adding every possible technology | The architecture becomes complex without solving concrete problems. | Introduce components only when requirements justify them. |
| Skipping scale estimates | There is no basis for decisions about storage, partitioning, caching, or throughput. | Estimate important orders of magnitude early. |
| Naming products instead of concepts | Technology names do not explain why the architecture works. | Explain the requirement, architectural pattern, and trade-off first. |
| Ignoring data access patterns | A database can be selected without considering how data is actually queried. | Define major reads and writes before choosing storage and indexes. |
| Ignoring failures | The design works only while every component is healthy. | Discuss failure detection, redundancy, retries, and degraded behavior. |
| Claiming a design is infinitely scalable | Every architecture eventually encounters another bottleneck. | Identify the next likely bottleneck and explain how the design evolves. |
| Giving decisions without trade-offs | The interviewer cannot see the reasoning behind architectural choices. | Explain both the benefit and cost of important decisions. |
| Trying to design everything | Time is spent on low-value details instead of important architectural decisions. | Build the high-level system, then deep-dive into the highest-risk areas. |
Interview Checklist
- Clarify the problem: identify the core features and explicitly narrow the scope.
- Define non-functional requirements: establish expectations for latency, availability, consistency, durability, and scale.
- Estimate important numbers: calculate approximate QPS, peak traffic, storage growth, bandwidth, or connections where relevant.
- Identify major APIs: define how clients interact with the system.
- Model important data: understand entities, relationships, access patterns, and indexes.
- Draw a simple architecture first: establish the end-to-end request flow before adding optimization layers.
- Explain every major component: connect caches, queues, replicas, and other components to concrete requirements.
- Find the bottlenecks: identify what fails or saturates as traffic increases.
- Discuss failure behavior: consider unavailable instances, databases, queues, caches, and networks.
- Address consistency: define where stale reads, duplicate processing, or eventual consistency are acceptable.
- Discuss partitioning: identify appropriate partition keys and potential hotspots when scale requires distribution.
- Include observability: explain which metrics reveal system health and backlog.
- State trade-offs explicitly: explain what each important decision improves and what complexity it introduces.
- Adapt to interviewer changes: evolve the architecture when requirements or scale assumptions change.
- Manage interview time: prioritize the areas with the greatest architectural impact.
Conclusion
System design interviews evaluate engineering reasoning more than architecture memorization. Requirements, scale, data access patterns, failure behavior, and trade-offs provide the foundation from which the architecture should emerge.
A strong design starts simple and becomes more sophisticated only where constraints require it. Caches, queues, replicas, partitioning, global routing, and other distributed-system techniques are valuable when they solve specific scalability or reliability problems, not because complex diagrams appear more impressive.
Key Takeaway
A system design interview is primarily a structured engineering conversation. Clarify the problem, estimate the scale, build the simplest architecture that satisfies the requirements, trace the important request flows, identify bottlenecks and failures, and explain the trade-offs behind every significant decision. The quality of the reasoning matters more than producing one supposedly perfect architecture.
More Articles to Read
- Estimating Scale and Capacity Planning
- Designing a URL Shortener
- Designing a Notification System
- Designing a Chat Application
- Designing a File Storage Service
- Common System Design Interview Mistakes
Comments (0)