How to Approach System Design
A structured approach to system design: clarify requirements → estimate capacity → define APIs → model data → design high-level architecture → deep-dive components → identify bottlenecks → discuss trade-offs. Following this skeleton prevents you from jumping straight into a solution and missing critical constraints.
Foundational.
How it works
System design problems are open-ended by design. 'Design Twitter' could mean anything — a toy project for 100 users or a global platform for 300 million. The first job is to narrow the problem. The second is to structure your thinking so you don't miss critical constraints.
The 8-step skeleton below works for interviews and real architecture reviews alike. Use it as a checklist, not a rigid script. Some steps will be quick (30 seconds); others will take 10 minutes. The order matters — each step constrains the next.
Functional requirements (what does the system do?):
- What are the core features? (e.g., 'users can post tweets, follow others, see a feed')
- What are the non-goals? (e.g., 'no recommendation algorithm in v1')
- Who are the users? (consumers, admins, API clients)
Non-functional requirements (how well must it do it?):
- Scale: How many users? How many requests per second?
- Latency: What's the acceptable response time? (50ms? 500ms?)
- Availability: What's the uptime target? (99%? 99.99%?)
- Consistency: Strong or eventual? (Can the feed be 5 seconds stale?)
- Cost: What's the budget? (startup vs enterprise)
- Security: Any special requirements? (PII, PCI, HIPAA)
This step takes 2-3 minutes in an interview and prevents 30 minutes of wasted design work. Interviewers want to see you ask these questions — it signals seniority.
Before drawing anything, ask: 'Do we need to support real-time feed updates, or is eventual consistency acceptable? What's the expected DAU — 100K or 300M? Is the feed personalized (algorithmic) or just chronological? Do we need search? What about media — images, video? What's the timeline — do tweets need to appear in feeds within 1 second of posting, or is 10 seconds OK?' The answers completely change the architecture.
Back-of-the-envelope math for 'Design Twitter' (300M DAU):
QPS (queries per second):
- 300M DAU × ~20 requests/day = 6B requests/day
- 6B / 86400 seconds ≈ 70,000 QPS average
- Peak = 3-5x average ≈ 200,000-350,000 QPS peak
Write QPS:
- 300M users × 0.1 tweets/day average = 30M tweets/day
- 30M / 86400 ≈ 350 tweets/sec average
- Peak (events, viral content): 3,500/sec
Storage:
- 30M tweets/day × 200 bytes (text) = 6GB/day text
- With media (10% have images, avg 500KB): 30M × 0.1 × 500KB = 1.5TB/day
- Yearly: ~550TB/year (media dominates)
Bandwidth:
- 70,000 reads/sec × 50KB per response = 3.5 GB/s = ~28 Gbps
- Peak: 100 Gbps (requires CDN)
Cache sizing:
- If 80% of traffic hits 20% of content (Pareto), cache the top 20% of active tweets
- Active tweets (last 7 days): ~210M × 200 bytes = ~42GB
- Cache 20% = ~8GB — easily fits in Redis
These numbers don't need to be exact — they need to be order-of-magnitude correct so you can choose the right technology. 300M DAU rules out a single PostgreSQL instance. 28 Gbps of egress means you need a CDN. 1.5TB/day of media means you need object storage (S3), not a database.
For Twitter:
POST /tweets — create a tweet (auth required)
GET /feed — get user's timeline (paginated)
POST /follow/:user — follow a user
DELETE /follow/:user — unfollow
GET /tweets/:id — get a single tweetAPIs reveal design decisions: do you need pagination? What's the auth model? Is it REST or something else?
- User: id, username, email, bio, created_at. → SQL (need transactions for auth, unique constraints).
- Tweet: id, user_id, text, media_urls, created_at. → Wide-column (Cassandra — massive writes, time-ordered).
- Follow: follower_id, followee_id, created_at. → Graph or SQL with composite index.
- Media: stored in S3, URL referenced in tweet.
- Timeline: pre-computed per user, stored in Redis (sorted set by timestamp).
This is where you decide between SQL, NoSQL, and hybrid. The data model constrains the architecture.
Client → CDN → Load Balancer → App Servers → (Cache + DB + Queue)Don't go deep yet. This is the skeleton.
Feed generation — how do you build a user's timeline when they follow 1,000 people?
- Fan-out on write (push model): when a user tweets, push the tweet ID into every follower's timeline cache. Pro: reads are O(1). Con: write amplification — a user with 30M followers writes 30M cache entries per tweet.
- Fan-out on read (pull model): when a user opens their feed, fetch tweets from all 1,000 people they follow, merge, sort. Pro: no write amplification. Con: reads are slow (1,000 queries).
- Hybrid: fan-out on write for normal users; fan-out on read for celebrities (30M followers).
Timeline cache — how do you store the feed in Redis? Sorted set by timestamp. Each user's feed is a sorted set of tweet IDs. Capped at the last 1,000 tweets.
This is where you show depth. Don't deep-dive every component — pick the ones that are genuinely hard.
- What if the primary database dies? → Promote a replica. How long does failover take? (30s-2min)
- What if a cache cluster dies? → System falls back to the database. Will it survive? (Probably not — cache absorbs 90% of reads. Need to degrade gracefully: throttle reads, serve stale data).
- What if traffic spikes 10x? → Auto-scale app servers. But database can't auto-scale. Need read replicas + connection pooling.
- What if a single user goes viral? → Their tweets get fanned out to millions. Use the hybrid approach: celebrities use fan-out on read.
- What if an entire region goes down? → Multi-region deployment. But cross-region replication has lag.
- 'I chose fan-out on write for low read latency, but it costs write amplification and makes deletion hard (must delete from every follower's cache).'
- 'I chose Cassandra for tweets because it handles massive write throughput, but I gave up JOINs and strong consistency — if a tweet and its media are stored separately, they can be inconsistent during a partition.'
- 'I chose Redis for timeline cache because it's fast, but it's in-memory — if Redis crashes, all timelines need to be rebuilt from the database.'
This step separates senior from junior — the ability to articulate trade-offs clearly.
The most common mistake is hearing 'design Twitter' and immediately saying 'use Kafka, Cassandra, and Redis'. Technology choices come AFTER you understand requirements, capacity, and data model. Naming technologies too early signals you're memorizing rather than reasoning. Start with 'I need a system that handles X writes/sec and Y reads/sec with Z latency' — then choose the technology that fits.
In a 45-minute interview, allocate time roughly:
- Requirements clarification: 3-5 min
- Capacity estimation: 3-5 min
- API + data model: 5-7 min
- High-level design: 5-7 min
- Deep dive: 10-15 min
- Bottlenecks + trade-offs: 5-10 min
- Q&A: remaining time
Don't spend 20 minutes on capacity estimation. Don't skip requirements. The interviewer will redirect you if you're going too deep on the wrong thing — listen to their cues.
You're asked to 'Design a URL shortener like bit.ly.' What should you do FIRST?
Pick one answer.
During a system design interview, you've drawn the high-level architecture. What should you do next?
Pick one answer.
Your system needs to serve 70,000 reads/sec for a Twitter-like feed. Which combination of technologies makes sense?
Pick one answer.
Capacity estimation, fully worked: 'Design a URL shortener'.
Assume the requirement: 100M new URLs per month, with a 100:1 read:write ratio.
Write QPS:
- 100M URLs / 30 days ≈ 38 URLs/sec average write rate
- Peak = 5x average = ~190 URLs/sec. Trivial — any database can do this.
Read QPS:
- 100M writes × 100 reads/write = 10B reads/month
- 10B / 2.59M seconds ≈ 3,860 reads/sec average
- Peak = 3-5x = ~12,000-19,000 reads/sec. Now we need engineering.
Storage:
- Each URL record: ~200 bytes (short code, long URL, user_id, timestamps)
- 100M URLs × 200 bytes = 20 GB/month = 240 GB/year
- 5-year horizon: ~1.2 TB. Fits on one machine, but you don't want to.
Bandwidth:
- Average read: ~50 bytes response (the long URL or a 301)
- 3,860 reads/sec × 50 bytes = ~193 KB/sec = trivial
- BUT if we include analytics (referrer, geo, user-agent): ~500 bytes per click event
- 10B click events/month × 500 bytes = 5 TB/month of analytics. Object storage territory.
Cache sizing (Pareto):
- 80% of traffic hits 20% of URLs (the viral ones)
- Hot set in any given day: ~20% of recent URLs × 100M/year ÷ 365 days ≈ 55K URLs/day hot
- 55K × 200 bytes = ~11 MB — fits in any Redis instance
- Cache hit rate target: 95%. The cache absorbs ~95% of reads.
Conclusion from these numbers:
- 19K reads/sec at peak: 3-5 app servers behind a load balancer (each handles 5K reads/sec)
- Database: 1 primary + 2 read replicas (since 95% of reads hit cache, only 5% × 19K = ~950 reads/sec hit DB)
- Analytics: write to a Kafka queue → consumer → S3/Cassandra (no need to write synchronously)
- Storage: 240 GB/year fits on a single Postgres instance for years, but plan for sharding by year-3.
Notice how the numbers DRIVE the architecture. 19K reads/sec demands multiple app servers + cache. 5 TB/month of analytics demands async + object storage. 38 URLs/sec write rate is trivial — no need for sharding, Kafka-for-writes, or exotic databases. The arithmetic pre-empts the technology choice.
(1) Jumping to technology — saying 'Kafka + Cassandra' before clarifying the read/write ratio. (2) Ignoring the interviewer's hints — they say 'what about failures?' and you keep drawing happy-path boxes. (3) Deep-diving the easy part — spending 10 minutes on the load balancer when the hard part is the timeline cache. (4) No trade-offs — naming technologies without saying what they cost. (5) Silence — designing in your head for 30 seconds while the interviewer stares at a blank whiteboard. Talk out loud, even your dead-ends.
You're designing a notification system that must send 10,000 push notifications per second at peak. The interviewer asks: 'Should we use Kafka or RabbitMQ?' What is the strongest first response?
Pick one answer.
Scaling a system design interview answer — depth beats breadth.
The single biggest mistake mid-level engineers make in a design interview is presenting a wide, shallow architecture: ten boxes, one paragraph each, no depth. Senior interviewers redirect: 'Pick the hardest component and design it in detail.' This is where you actually demonstrate system-design skill.
A useful rule: spend 30% of your time on the high-level diagram and 50% on one deep dive. For 'Design Twitter', don't try to design the API, the user service, the notification service, AND the timeline cache at depth — pick the timeline cache. Discuss fan-out-on-write vs fan-out-on-read; explain the celebrity problem (a user with 30M followers would amplify every tweet to 30M cache entries); propose the hybrid (normal users fan-out on write, celebrities fan-out on read); show the Redis sorted-set data structure and explain why it's O(log N) for insertion; discuss cache invalidation when a tweet is deleted.
The reason depth matters: shallow designs are interchangeable — anyone can list 'CDN, load balancer, app servers, database'. Depth reveals the trade-offs and the understanding. An interviewer learns nothing from a wide diagram they couldn't have drawn themselves. They learn whether you can reason about a specific hard problem by watching you deep-dive it.
Engineering mental model
Mental model. Think of How to Approach System Design as a deliberate boundary in a system. The boundary exists because something becomes harder to manage when everything is done in one place: latency, scale, failure isolation, consistency, cost, or team ownership. The useful question is not “what does How to Approach System Design mean?” but “what pressure makes this boundary worth introducing, and what new failure mode does it create?”
Before choosing How to Approach System Design, name the workload, the critical user path, the dominant bottleneck, the failure you are trying to absorb, and the trade-off you are willing to accept. If you cannot name those five things, the design is probably premature.
// Pseudocode
request = receive()
result = how_to_approach_system_design(request)
return result
// Production questions:
// 1. What happens on timeout?
// 2. Can this operation be retried safely?
// 3. What is the bottleneck?Back-of-the-envelope reasoning
Numerical lens: write down traffic, payload size, read/write ratio, peak multiplier and durability target before choosing a component. The numbers should justify the architecture.
Interactive thought experiment: How to Approach System Design
Change the variables below and predict what breaks first in How to Approach System Design. The production lab can later reuse these same inputs.
Change one variable at a time. Predict the failure mode first, then move the slider and see whether your mental model matches the simplified system response.
If you are stuck on How to Approach System Design, start by drawing the request path and marking every network hop, stateful component, queue, cache and failure boundary. Then estimate where the system will saturate.
You increase traffic by 10× in a system using How to Approach System Design. What should you inspect first?
Pick one answer.
Which statement is the safest engineering habit when using How to Approach System Design?
Pick one answer.
You have dashboards for traffic, latency, errors and saturation. You can change the architecture, but every change has operational cost.
Production scenario: your system uses How to Approach System Design, traffic suddenly spikes, and p99 latency doubles. What is your first move?
Interview drill
Answer this without notes: When would you choose How to Approach System Design, and when would you intentionally avoid it? Mention at least one bottleneck it addresses, one failure mode it introduces, and one alternative. Then quantify the workload you are designing for.
A useful engineering lens for How to Approach System Design: define the problem it solves, the simpler design that fails first, the constraint that forces you to introduce this concept, and the new failure modes the concept creates.
Numerical sanity check
Back-of-the-envelope reasoning beats fake precision. State your traffic, payload, concurrency and growth assumptions explicitly, then calculate enough to know whether the current architecture is orders of magnitude away from the target.
Do not optimize for a memorized definition. Reason from the workload and failure mode.
Imagine the simplest version of a system using How to Approach System Design. What breaks first as traffic grows by 10×, and what would you change before reaching 100×?
Pick one answer.
What you gain, what you pay
- +Prevents premature commitment to a technology.
- +Surfaces constraints before they become incidents.
- +Creates a shared structure that teams and interviewers can follow.
- +Makes trade-offs explicit, so decisions are defensible.
- −Can feel rigid — real systems sometimes need creative leaps.
- −Takes 30-45 minutes; not every problem deserves full treatment.
- −Can produce over-engineered designs if you follow every step dogmatically.
How this breaks in production
- Skipping requirements clarification → designing the wrong system.
- Jumping to technology before understanding data and scale.
- Deep-diving every component instead of the 1-2 hard ones.
- Not articulating trade-offs — making the design feel arbitrary.
Don't fall into these traps
- •Treating the 8 steps as a script instead of a checklist.
- •Estimating capacity with made-up numbers instead of deriving them from requirements.
- •Forgetting to discuss failure modes — interviewers always ask 'what breaks?'
- •Choosing 'cool' technologies (Kafka, Cassandra) when simpler ones (PostgreSQL, Redis) would work.
Real systems using this
How real systems implement this
- Google design docs — Google's engineering culture requires design docs that follow this structure: requirements, alternatives, proposed design, trade-offs. Documented in their engineering practices guide.
- AWS Well-Architected Framework — AWS follows the same skeleton: requirements, capacity, design, bottlenecks, trade-offs. Their 5 pillars (operational excellence, security, reliability, performance efficiency, cost optimization) map to the non-functional requirements you clarify in step 1.
Practice saying it out loud
- Q1Walk me through how you would approach designing a system you've never seen before.
- Q2What questions do you ask before starting a system design?
- Q3Why is capacity estimation important before choosing a database?
- Q4How do you decide which components to deep-dive vs. which to leave at the high level?
Further reading & references
Core explanations are original NO CAP material. External references are provided for deeper study and standards.
What next?
Mark as understood once the mental model clicks.
Next recommended
Performance vs Scalability