ZTechUniverse

System Design Interview Questions with Answers

2025-02-22 · 4 min read

TL;DR — System design interviews test how you think about scale, trade-offs, and components. Start by clarifying requirements, then draw a high-level design. Here are common questions with structured answers.


1. Design a URL Shortener (e.g. bit.ly)

Clarify: 10M URLs/sec? 1B URLs stored? Short links expire? Custom slugs?

High-level design:

[Client] → [Load Balancer] → [App Servers] → [Database] [Cache (Redis)]

Key decisions:

  • Short code — Base62 encode a random ID or auto-increment. 6 chars ≈ 56B combinations.
  • Storage — DB: (short_code, long_url, created_at). Index on short_code.
  • Cache — Redis: short_code → long_url. Read-through cache. Hot URLs stay in cache.
  • Redirect — 302 (temporary) so analytics work; 301 (permanent) for SEO.

Code: Generate short code

TypeScript
function generateShortCode(): string { const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; let code = ""; for (let i = 0; i < 6; i++) { code += chars[Math.floor(Math.random() * chars.length)]; } return code; } async function createShortUrl(longUrl: string): Promise<string> { const code = generateShortCode(); await db.urls.insert({ code, longUrl, createdAt: new Date() }); return `https://short.com/${code}`; }

2. Design a Rate Limiter

Clarify: Per user or per IP? Sliding window or fixed? Distributed?

Approaches:

  • Fixed window — Count requests per minute. Simple but allows burst at edge.
  • Sliding window — Use Redis sorted set. Remove old entries; count in window.
  • Token bucket — Refill tokens at rate. Allow burst up to bucket size.

Code: Sliding window with Redis

TypeScript
async function isAllowed(userId: string, limit: number, windowSec: number): Promise<boolean> { const key = `ratelimit:${userId}`; const now = Date.now(); const windowStart = now - windowSec * 1000; await redis.zremrangebyscore(key, 0, windowStart); const count = await redis.zcard(key); if (count >= limit) return false; await redis.zadd(key, now, `${now}-${Math.random()}`); await redis.expire(key, windowSec); return true; }

3. Design a News Feed (e.g. Twitter timeline)

Clarify: 300M users? Read-heavy or write-heavy? Real-time or eventual?

Approaches:

  • Pull (fan-out on read) — On read, fetch posts from followed users. Simple but slow for users with many followees.
  • Push (fan-out on write) — On post, push to all followers' feeds. Fast read, slow write for celebrities.
  • Hybrid — Push for normal users; pull for celebrities (e.g. >10K followers).

Storage:

  • Posts(post_id, user_id, content, created_at)
  • Feed — Redis sorted set per user: feed:user123{post_id: score (timestamp)}
  • TimelineZREVRANGE feed:user123 0 49 for latest 50 posts.

4. Design a Distributed Cache

Clarify: Cache size? Eviction policy? Consistency?

Key concepts:

  • Consistent hashing — Add/remove nodes without remapping all keys.
  • Replication — Each key on N nodes for fault tolerance.
  • Eviction — LRU (Least Recently Used). Redis uses approximate LRU.

Consistency:

  • Cache-aside — App reads from cache; on miss, read DB and populate cache. On write, invalidate cache.
  • Write-through — Write to cache and DB together. Cache is source of truth for reads.

5. Design a Chat System (e.g. WhatsApp)

Clarify: 1:1 or group? Real-time? Message persistence?

Components:

  • WebSocket/HTTP long-polling — Real-time delivery.
  • Message queue — Persist messages; fan-out to online recipients.
  • Storage — Messages in DB. Index by (conversation_id, timestamp).

Flow:

  1. User sends message → API stores in DB and pushes to queue.
  2. Queue consumer fans out to online users in the conversation.
  3. WebSocket server pushes to connected clients.

6. How would you scale a database?

Options:

  • Read replicas — Replicate writes to multiple read replicas. Scale reads.
  • Sharding — Partition data by key (e.g. user_id). Each shard is a separate DB.
  • CQRS — Separate read and write models. Write to one store; read from optimized read store.

Sharding example:

TypeScript
function getShard(userId: string): number { const hash = hashCode(userId); return Math.abs(hash) % 4; // 4 shards } const shard = getShard(userId); const db = shards[shard]; const user = await db.users.findById(userId);

Summary

QuestionKey components
URL shortenerLB, app servers, DB, cache, Base62 encoding
Rate limiterRedis, sliding window or token bucket
News feedFan-out on read vs write, hybrid for celebrities
Distributed cacheConsistent hashing, replication, eviction
ChatWebSocket, message queue, persistence
DB scalingRead replicas, sharding, CQRS

For more on system design, see System Design Beginner Guide.