Comprehensive reference for system design interview prep covering essential concepts, practical examples, and production best practices.
System design interviews test your ability to turn a vague requirement into a robust, scalable, maintainable system under time pressure. You’re expected to design a real-world service—like a URL shortener, social media feed, or real-time chat—starting from a single sentence. This guide walks you through the process of preparing for and executing such interviews, with precise, reusable techniques, patterns, and common pitfalls.
Begin by extracting clear requirements from the interview prompt. Repeat the problem aloud. Identify:
Write down:
Use the 5 Ws framework:
Rank features by value and complexity. Use the MoSCoW method:
Example: For a photo-sharing app:
This guides your design depth: spend more time on must-haves, sketch lightly on could-haves.
Sketch a conceptual data model using entity-relationship notation. Use plain text or a whiteboard.
Entities:
Relationships:
Decide on the primary storage: relational database, document store, or key-value store.
For a feed-heavy app, relational is common:
CREATE TABLE posts (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL REFERENCES users(id),
content TEXT,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ
);
CREATE INDEX idx_posts_user_id ON posts(user_id);
CREATE INDEX idx_posts_created_at ON posts(created_at);
For high-read, low-write workloads (e.g., user profiles), document store (e.g., MongoDB) may be better:
{
"user_id": 12345,
"name": "Alice Chen",
"email": "[email protected]",
"profile": {
"bio": "Software engineer focused on distributed systems.",
"location": "San Francisco"
},
"followers": [6789, 101112],
"created_at": "2023-05-10T14:23:00Z"
}
Use denormalization to reduce joins:
user_name and user_avatar directly in posts tableusers table for every postPlan for schema changes without downtime.
Example: adding a thumbnail_url to Posts:
posts tableUse REST conventions. Use standard status codes:
200 OK for successful GET201 Created on POST204 No Content on PUT/DELETE400 Bad Request on validation failure404 Not Found, 409 Conflict, 429 Too Many RequestsExample endpoints:
GET /api/posts
GET /api/posts/{id}
POST /api/posts
PUT /api/posts/{id}
DELETE /api/posts/{id}
GET /api/users/{id}/feed
POST /api/posts/{id}/like
GET /api/posts/{id}/comments
Use URL-based versioning:
/api/v1/posts/api/v2/postsInclude version in response headers:
HTTP/1.1 200 OK
Content-Type: application/json
X-API-Version: 2.1
Use JWT (JSON Web Tokens):
Authorization: Bearer <token> headerJWT payload:
{
"user_id": 12345,
"name": "Alice Chen",
"email": "[email protected]",
"exp": 1700000000,
"iat": 1699990000,
"scope": ["read", "write"]
}
Use scopes for fine-grained permissions:
read:posts, write:posts, admin:usersApply token bucket algorithm:
429 Too Many RequestsResponse:
{
"error": "Too Many Requests",
"message": "Rate limit exceeded",
"retry_after": 60
}
Use microservices for complex systems.
Split the system into services:
Each service runs independently, with its own database.
Use API gateway to route requests:
/api/posts → Post service/api/users/feed → Feed serviceSketch the request path from client to database.
Example: fetching a feed:
GET /api/users/12345/feedUse caching strategy:
Use read replicas:
Use database sharding:
user_idUse consistent hashing to distribute data across shards.
Use load balancer (e.g., Nginx, HAProxy) to distribute requests across service instances.
Deploy multiple instances behind a load balancer:
upstream post_service {
server 10.0.1.10:8080;
server 10.0.1.11:8080;
server 10.0.1.12:8080;
}
server {
listen 80;
location /api/posts {
proxy_pass http://post_service;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Use connection pooling in database clients.
Design for partial failures:
Use health checks:
/health endpoint returns 200 OK if service is healthyExample health check response:
{
"status": "healthy",
"database": {
"connected": true,
"latency_ms": 12
},
"cache": {
"connected": true,
"hit_rate": 0.94
},
"uptime_seconds": 3642,
"timestamp": "2023-10-15T10:30:00Z"
}
Use containerization with Docker:
FROM golang:1.21-alpine
WORKDIR /app
COPY . .
RUN go build -o main main.go
EXPOSE 8080
CMD ["./main"]
Use Docker Compose for local development:
version: '3.8'
services:
api-gateway:
build: ./api-gateway
ports:
- "80:80"
depends_on:
- post-service
- user-service
post-service:
build: ./post-service
ports:
- "8080:8080"
environment:
- DB_HOST=postgres
- REDIS_HOST=redis
depends_on:
- postgres
postgres:
image: postgres:15
environment:
POSTGRES_DB: app
POSTGRES_USER: user
POSTGRES_PASSWORD: password
volumes:
- pgdata:/var/lib/postgresql/data
redis:
image: redis:7
ports:
- "6379:6379"
volumes:
pgdata:
Use whiteboard or slides:
Answer: “Why did you choose this?”
Examples:
Prepare for common follow-ups:
Practice answering:
Listen carefully. Refine your design based on feedback.
If asked, “Can you make the feed update in real time?”:
Use logging and monitoring:
{
"timestamp": "2023-10-15T10:30:00Z",
"trace_id": "a1b2c3d4",
"service": "feed-service",
"method": "GET",
"path": "/api/users/12345/feed",
"status": 200,
"duration_ms": 142,
"user_id": 12345,
"num_posts": 25
}
Always prepare rollback plan:
This guide is not just a template—it’s a playbook for building confidence and clarity in every system design interview.
This page was rewritten on 18 October 2026. It replaced a templated version that was not specific to its own title. How these pages are written and checked. If anything here is wrong, tell us at [email protected] and we will correct it.
We use cookies for analytics (Google Analytics) and advertising (Google AdSense) to improve your experience and support free content. Privacy Policy