System design interviews can feel intimidating because the questions are intentionally broad. You may be asked to design a social network, messaging platform, video-streaming service, notification system, or URL shortener. Each problem is different, but the method used to approach it is largely the same.
The interviewer is not expecting you to reproduce the exact architecture used by a large technology company. They want to understand how you break down an ambiguous problem, make engineering decisions, identify bottlenecks, and communicate trade-offs.
In this post, we’ll walk through a structured approach you can use in almost any system design interview.
A system design interview normally begins with a short and deliberately vague prompt:
Design a service like Instagram.
That single sentence leaves many unanswered questions. Are we designing photo uploads, the news feed, direct messaging, search, or the entire platform? How many users will the system support? Does it need to operate globally? How quickly should new content appear?
Trying to design everything immediately is one of the most common mistakes candidates make. A better approach is to clarify the requirements, agree on the scope, estimate the scale, and then develop the architecture one layer at a time.
The objective is not to produce a perfect system. It is to show that you can move from an unclear problem to a sensible, scalable design.
Before drawing any architecture, make sure you understand what you are being asked to build.
Begin with the functional requirements. These describe what users must be able to do. For a photo-sharing service, users might need to upload photos, follow other users, view a feed, like posts, leave comments, and delete their own content.
Avoid expanding the system unnecessarily. Features such as direct messaging, advertising, search, video processing, and recommendations may be important in a real product, but including all of them could make the interview too broad. Agree with the interviewer on which features are in scope and which can be left for later.
You should also clarify the non-functional requirements. Ask how many users the system must support, what response time is acceptable, whether it needs to work across multiple regions, and how durable the data must be. It is also important to establish whether the system should favour consistency or availability when failures occur.
These requirements will influence almost every architectural decision that follows. A financial transaction system, for example, will normally prioritise correctness and strong consistency. A social media feed may accept slightly stale results in exchange for higher availability and lower latency.
Requirements are not simply an introduction to the solution. They are the basis for the solution.
Once the scope is clear, describe the most important user journeys.
For a messaging system, the primary workflow begins when a user sends a message. The system validates and stores it, delivers it to the recipient, sends a notification if necessary, and eventually records its delivery and read status.
Thinking in workflows keeps the architecture connected to real behaviour. It also helps reveal which operations are read-heavy, write-heavy, synchronous, or suitable for background processing.
A video platform, for example, may receive relatively few uploads compared with the enormous number of video views it serves. This suggests that media delivery and caching will be more important to its scaling strategy than upload throughput.
Focus on the two or three workflows that matter most. Secondary features can be revisited after the core system has been designed.
You usually do not need exact figures, but a few back-of-the-envelope calculations can prevent unrealistic decisions.
Suppose a service has 10 million daily users and each user makes an average of 20 requests per day:
10,000,000 × 20 = 200,000,000 requests per day
Dividing this by the number of seconds in a day gives an average of roughly 2,300 requests per second. Traffic is rarely evenly distributed, so the peak might be several times higher. Designing for 10,000 requests per second would therefore be more realistic than designing only for the average.
Storage can be estimated in a similar way. If one million images are uploaded each day and the average compressed image is two megabytes, the service will generate roughly two terabytes of new media every day before replication and backups are considered.
These estimates help determine whether the system needs a single database, read replicas, horizontal partitioning, object storage, or a content delivery network.
The purpose is not mathematical precision. State your assumptions, arrive at a reasonable order of magnitude, and use it to guide the design.
Before designing individual services, identify the main interface the system exposes.
A simplified photo-sharing API could contain operations for creating a post, retrieving a post, loading a user’s feed, liking a post, and deleting content:
POST /posts
GET /posts/{postId}
GET /users/{userId}/feed
POST /posts/{postId}/likes
DELETE /posts/{postId}
You do not need to document every request and response field. Focus on the operations needed to support the core requirements.
Next, identify the main data entities. In this example, they might include users, posts, media files, follow relationships, likes, comments, and feed entries. Consider how these entities relate to each other and, more importantly, how they will be accessed.
A relational database may work well for structured records and transactional operations. A key-value or document store may be better for workloads that require flexible schemas or very high horizontal throughput. Images and videos should generally live in object storage rather than directly inside the primary database.
Do not choose a database simply because it is popular. Explain how its characteristics match the system’s data and access patterns.
Once the foundations are clear, sketch the major components and show how a request moves through them.
A typical internet-scale architecture might look like this:
Clients
↓
Load Balancer
↓
Application Services
├── Cache
├── Primary Database
├── Object Storage
└── Message Queue
↓
Background Workers
Web and mobile clients send requests through a load balancer, which distributes traffic across multiple application servers. This prevents any single server from receiving all the traffic and allows unhealthy instances to be removed without taking down the whole service.
The application layer handles authentication, validation, permissions, and business logic. A smaller system may begin as one application, while a larger system may separate responsibilities such as users, posts, feeds, notifications, and media processing into different services.
Services should be separated when there is a clear scaling, reliability, or organisational reason to do so. Microservices are not automatically better than a well-structured application, and introducing them too early can create unnecessary operational complexity.
Frequently requested data can be stored in an in-memory cache to reduce database load and improve response times. Durable application records remain in the primary database, while large files such as images and videos are stored in object storage and distributed through a content delivery network.
A message queue can be introduced for work that does not need to finish before the user receives a response. Sending notifications, resizing images, processing videos, updating search indexes, and collecting analytics are all good candidates for background processing.
At this stage, keep the design simple. The interviewer should be able to understand the complete architecture before you begin exploring individual components.
The deep dive is where you examine the most important or difficult part of the system. The interviewer may choose the area, or you may suggest one based on the requirements.
For a social network, the discussion might focus on feed generation. For a messaging platform, it might focus on real-time delivery and message ordering. A file-storage system may require a deeper discussion about replication and durability.
A database commonly begins as a single primary instance. As traffic grows, read replicas can distribute query traffic across additional servers. This works well for read-heavy systems, although replicas may return slightly stale data because updates take time to propagate.
When a dataset becomes too large for a single server, it may need to be partitioned or sharded. Sharding distributes subsets of the data across independent database servers.
The shard key is an important design decision. It should distribute data evenly, support common access patterns, and avoid concentrating traffic on a small number of servers.
Using a user ID as the shard key can work well for user-centred data because most requests can be routed directly to the correct shard. However, queries involving many users may then require requests to several shards. This is a trade-off that should be acknowledged rather than hidden.
Adding a cache box to the architecture is not enough. You should explain how it will be used and how cached data will remain reasonably current.
A common approach is cache-aside. The application first checks the cache and, if the value is missing, reads it from the database. The result is then placed in the cache so later requests can retrieve it more quickly.
This approach reduces database traffic, but it creates new questions. Cached entries may become stale, popular keys may receive too much traffic, and many requests may try to rebuild the same expired entry simultaneously.
Not every value needs the same level of freshness. A follower count may be allowed to remain slightly out of date, while account permissions or security settings may require immediate invalidation.
The consistency requirements should determine the caching strategy.
Slow work that does not affect the immediate response can often be moved to a message queue.
When a user uploads an image, for example, the system can store the original file, save its metadata, publish an image-processing event, and return a successful response. Background workers can then create thumbnails, optimise the file, and run content checks without making the user wait.
This improves response times and allows processing capacity to scale independently from the main application.
Asynchronous processing also introduces new failure cases. Messages may be delivered more than once, workers may crash, and events may be processed out of order. Workers should therefore be designed to be idempotent, meaning that processing the same message multiple times does not produce an incorrect result.
Distributed systems frequently require a trade-off between immediately consistent data and continued availability during failures.
Strong consistency is important when stale information could cause serious harm. Financial balances, inventory reservations, access-control changes, and username ownership are common examples.
Eventual consistency is often acceptable for view counts, activity feeds, analytics, search indexes, and recommendation data. A short delay in these areas normally has little effect on the user.
Rather than describing the entire system as strongly or eventually consistent, discuss the requirements of individual operations. Different parts of the same system can make different choices.
A production system must continue operating when individual components fail.
Application services should normally run as multiple instances so traffic can be redirected if one of them becomes unhealthy. Important data should be replicated, and databases should have a tested backup and recovery process. Requests to other services should use timeouts so that one slow dependency does not consume resources indefinitely.
Retries can help recover from temporary failures, but they must be bounded and used carefully. Retrying a failed read is usually safe. Retrying a payment or order request without an idempotency key could perform the operation twice.
Message queues also require a failure strategy. If a worker cannot process an event after several attempts, the event can be moved to a dead-letter queue for inspection rather than being retried forever.
You should also consider graceful degradation. If a recommendation service becomes unavailable, the application might show recent or popular content instead. A non-essential feature can fail without making the entire product unusable.
Security is sometimes overlooked during system design interviews, but it affects the architecture from the beginning.
The system should authenticate users and verify that they are authorised to access each resource. Sensitive traffic should be encrypted, inputs should be validated, and public endpoints should be protected with rate limits.
The design may also need to address privacy controls, data retention, deletion requests, secrets management, audit logs, spam, and abusive behaviour. A service containing private media must ensure that object-storage URLs cannot be used to bypass application permissions.
These details should remain proportional to the problem. You do not need to turn every interview into a complete security review, but acknowledging the most important risks shows that you are thinking about a real production system.
Once a system is running, engineers need to understand whether it is healthy and how users are experiencing it.
Metrics can show request latency, traffic, error rates, queue depth, cache hit rate, database load, and resource saturation. Structured logs provide details about individual events, while distributed tracing helps follow a request across several services.
Monitoring should be connected to the user experience. Measuring successful message deliveries, completed uploads, or correctly loaded feeds is often more valuable than monitoring CPU usage alone.
Observability can also validate architectural decisions. If a cache was introduced to reduce database load, its hit rate should confirm whether it is actually working. If background workers are falling behind, queue depth should make the problem visible before users begin to notice long delays.
Communication is as important as technical knowledge during a system design interview.
Begin by restating the agreed requirements and assumptions. Present the high-level architecture, then follow one important request through the system from beginning to end. Once the overall design is clear, explore the most important technical challenge and discuss how the system behaves under load or during failures.
Explain the reasoning behind each significant decision. You might use object storage because the system handles large immutable files, introduce a queue so image processing does not delay uploads, or accept eventual consistency because a short delay in feed updates is harmless.
If the interviewer challenges a decision, treat it as an invitation to explore the design. Most architectural choices have disadvantages, and strong candidates acknowledge them.
Finish by summarising how the proposed system meets the original requirements. This brings the conversation back to the problem instead of leaving it focused on one database, queue, or caching strategy.
One of the most common mistakes is beginning the architecture before clarifying the problem. This can result in a technically impressive solution that solves the wrong set of requirements.
Another mistake is naming technologies without explaining their purpose. Saying that the system uses Kafka, Redis, Cassandra, and Kubernetes does not explain the design. Every technology should address a specific requirement, access pattern, or bottleneck.
Candidates also sometimes design for infinite scale. A service for ten thousand users does not need the same architecture as one serving hundreds of millions. Complexity should be justified by the expected traffic and reliability requirements.
Database choices should follow the data access patterns rather than personal preference. Think about how records will be written, queried, updated, and deleted before deciding where to store them.
Finally, avoid going too deep too early. Spending half the interview designing a database schema before presenting the rest of the system makes it difficult for the interviewer to understand the overall solution. Establish the full architecture first, then deepen the areas that matter most.
Once the core design works, discuss how it could evolve.
The first improvement should usually address the system’s most likely bottleneck. A read-heavy service might add caching or database replicas. A system processing large amounts of background work might add more queue partitions and workers. A media-heavy service could introduce a CDN to reduce latency and bandwidth usage.
Reliability can be improved by deploying services across multiple availability zones, replicating data, automating failover, and regularly testing backups. If global availability is required, the system may eventually expand into several regions, although this creates additional challenges around data consistency and routing.
The architecture can also become more cost-efficient over time. Old data might be moved into cheaper storage, caches can be sized according to measured usage, and services can scale automatically as traffic changes.
Prioritise these improvements rather than presenting them as a collection of unrelated ideas. A sensible order is to remove the current bottleneck, strengthen reliability, prepare for expected growth, and then optimise cost.
Good architecture is not the architecture with the most components. It is the simplest design that satisfies the requirements while providing a credible path for future growth.
A successful system design interview is a structured conversation rather than a search for one perfect diagram.
Start by clarifying the functional and non-functional requirements. Define the scope, identify the main user workflows, and estimate the expected traffic and storage. From there, describe the APIs and data model before presenting a simple high-level architecture.
Once the complete system is understandable, explore its most important component in depth. Explain the decisions around data storage, caching, asynchronous processing, consistency, reliability, security, and monitoring. Be open about trade-offs and describe how the system could evolve as its requirements change.
The most important skill is not memorising how well-known companies built their systems. It is learning how to take an ambiguous problem, establish sensible assumptions, and develop a design whose decisions can be clearly explained.
That is what the system design interview is really testing.