Phase 5
High Level Design
Fresher-level system design. Concepts first, then five classic systems.
Phase 5
High Level Design
Fresher-level system design. Concepts first, then five classic systems. · 2–3 weeks
Client-server architecture
Browser/app talks to an API. Stateless servers. Why this is the default.
Client-server means the client (browser, app) sends requests to a server that owns data and business rules. Stateless servers do not remember you between HTTP requests — session lives in a cookie, token, or store. That is the default because you can add identical instances behind a load balancer. The alternative is sticky in-memory session, which makes scaling and failover harder.
Requirements
- One-sentence definition
- Where state lives
More reading
Load balancing
Spread traffic across instances. L4 vs L7 at a high level.
A load balancer sits in front of many app instances and spreads requests so no single box is the bottleneck and you can take instances down. L4 (TCP) balances connections; L7 (HTTP) can route on path or host. Health checks drop dead backends. Round-robin, least-connections, and consistent hashing are the usual policies. Without an LB, “add another server” has nowhere to send traffic.
More reading
Horizontal vs vertical scaling
Bigger machine vs more machines. Where each hits a wall.
Vertical scaling is a bigger machine (more CPU/RAM) — simple, hits a hardware ceiling, and is a single point of failure. Horizontal scaling is more machines behind a balancer — needs stateless apps and shared data stores. Freshers should say: scale out the web tier first; databases scale out later (replicas, then shards) because data has gravity. Cost and failure domains differ, not just “faster.”
More reading
Caching
What to cache, TTL, cache stampede, where (CDN, Redis, app).
A cache stores a computed or fetched result so the next read is cheaper. Put it at the CDN (static), Redis (shared hot keys), or in-process (fast, not shared). TTL and invalidation decide staleness. Cache stampede is many misses hitting the DB at once when a key expires — mitigate with lock, slightly random TTL, or serving stale. Cache what is read often and expensive; do not cache what must be correct to the millisecond unless you have a plan.
More reading
Redis
In-memory store. Cache, sessions, rate limits, simple queues.
Redis is an in-memory key-value store with useful types (strings, hashes, lists, sorted sets) and optional persistence. Typical uses: cache, session store, rate-limit counters, leaderboards, simple queues. It is not your system of record for money unless you design durability on purpose. Interview sentence: “hot path reads go to Redis; source of truth stays in Postgres.”
More reading
Database indexing
Same idea as DBMS indexes, now in a system-design sentence: reads vs writes.
In HLD, indexes are how you keep reads fast as tables grow: you design access patterns (lookup by user_id, time range) and add indexes to match. Every index slows writes and uses disk. “We’ll shard” is the wrong first answer to a slow query — check the query and the index. Say which column you index and why, not “we add indexes.”
More reading
SQL vs NoSQL
Joins and transactions vs flexible scale. Pick based on access patterns, not hype.
SQL (Postgres/MySQL) gives schemas, joins, and ACID transactions — default for user accounts, orders, money. NoSQL (document, key-value, wide column) trades some of that for flexible documents or easier horizontal scale on simple access patterns. Pick from how you query and whether you need multi-row transactions, not from trend. Many systems use SQL plus Redis, not “NoSQL because scale.”
More reading
Replication
Primary + replicas. Read scaling. Failover at a high level.
Replication copies data from a primary to replicas. Reads can go to replicas; writes go to the primary (typical). Failover promotes a replica if the primary dies — there is a window of possible data loss with async replication. Replicas lag; stale reads are the tradeoff for read scale. This is how you scale reads before sharding.
More reading
Sharding
Split data by key. Hot partitions. Why you postpone this as a fresher.
Sharding splits a dataset across machines by a key (user_id % N, hash ranges). Each shard is a smaller database. Cross-shard joins and transactions get hard; a hot key (celebrity user) overloads one shard. Freshers should postpone sharding: indexes, caching, and replicas usually come first. If you shard, say the key and how you avoid hotspots.
More reading
Message queues
Decouple producers and consumers. Buffer spikes. At-least-once delivery.
A queue sits between a producer and workers: the API enqueues “send email” and returns; workers consume at their pace. That decouples spikes from slow I/O and lets you retry. Most queues are at-least-once — design consumers to be idempotent. Use a queue when work can be async; do not queue the user’s login response.
More reading
Kafka
Log of events, consumer groups. Enough to say why it's used, not how to operate a cluster.
Kafka is a durable, ordered log of events partitioned by key. Producers append; consumer groups share partitions so each message is processed by one consumer in the group, and you can replay. It is for high-throughput event streams and multiple independent consumers, not a simple job queue (though people use it that way). As a fresher, say “append-only log, replay, fan-out” — not broker tuning.
More reading
CDN
Cache static assets close to users.
A CDN caches static files (JS, CSS, images, video segments) on edge servers near users so origin is barely hit. TTL and cache keys matter; HTML is often shorter-lived than hashed assets. YouTube/Netflix HLD is mostly CDN + object storage. Dynamic API calls still go to your region unless you add more machinery. Interview line: put the CDN in front of blobs, not as a magic DB.
More reading
Rate limiting
Token bucket / sliding window at a high level. Protect APIs.
Rate limiting caps how many requests a client (IP, user, API key) can make in a window so one caller cannot exhaust the API. Token bucket allows short bursts; sliding window is smoother than fixed windows. Redis is the usual shared counter. Return 429. Place it at the gateway. It is protection, not a substitute for horizontal scale.
More reading
CAP theorem
Partition happens. You trade consistency vs availability. Don't over-apply it.
CAP says if the network partitions, a distributed store cannot be both fully consistent and fully available — you choose. Most interview answers over-apply it to a single Postgres. Use it when you have replicas across failure domains and must say whether you serve stale data or error. PACELC (latency vs consistency when the network is fine) is the honest extra. Do not recite CAP as the design.
More reading
Consistency
Strong vs eventual. What the user sees after a write.
Consistency here is what a read returns after a write. Strong: the next read (in the agreed scope) sees the write. Eventual: replicas catch up; a read might be stale for a while. Users notice this as “I liked the post but the count didn’t move.” Pick strong for money and inventory; eventual is fine for counts and feeds if you say the lag. Isolation levels in one DB are a related but different topic.
More reading
Availability
Uptime, redundancy, health checks. nines as a talking point, not a religion.
Availability is the fraction of time the system successfully serves requests. You buy it with redundancy (multi-AZ, replicas, load balancers) and health checks that stop sending traffic to dead boxes. “Three nines” is ~8 hours down per year — a talking point, not a design. Single primary DB without failover is the usual fresher hole. Measure user-facing success, not just process uptime.
More reading
Reliability
Retries, timeouts, idempotency. Things fail; your design should expect it.
Reliability is behaving correctly under failure: networks drop, timeouts fire, workers crash mid-job. Timeouts bound waits; retries with backoff handle blips; idempotency keys make retries safe. At-least-once delivery plus idempotent consumers is the standard story. A design that assumes every RPC succeeds is not a design. This is more useful in a fresher HLD than naming six new databases.
More reading
URL Shortener
Create short links, redirect, analytics. The default fresher HLD question.
A URL shortener maps a short code to a long URL: POST creates the mapping, GET redirects (301/302). The read path is extremely hot, so cache codes in Redis and generate unique IDs (hash or ticket server) without collisions. Analytics are optional counters. Do not start with sharding; a single DB plus cache handles a lot. This is the default fresher HLD because it is small and still has IDs, cache, and redirects.
Requirements
- Create short URL
- Redirect 301/302
- Optional click counts
Components
- API
- DB for mappings
- Cache for hot keys
- unique ID generator
Data flow. Client → API → write mapping → on GET, lookup cache then DB → 302.
Scaling. Cache reads. Hash or ticket IDs. Don't start with sharding.
Twitter / Instagram Feed
Follow graph + timeline. Fan-out on write vs read.
A social feed is posts plus a follow graph and a home timeline. Fan-out on write pushes a post into each follower’s cache (fast read, painful for celebrities). Fan-out on read pulls from followees at read time (cheap write, slow read). Hybrid (fan-out normal users, pull celebrities) is the honest answer. Storage of posts is separate from the precomputed timeline cache.
Requirements
- Post
- Follow
- Home timeline
Components
- User service
- Post service
- Fan-out / timeline cache
Data flow. Publish post → store → push to followers' caches or pull at read time.
Scaling. Celebrities break fan-out-on-write. Hybrid is the honest answer.
1:1 messages, delivery receipts, online status. Connection-heavy.
Chat is connection-heavy: clients keep a long-lived connection (WebSocket) to a gateway so messages push instantly. If the recipient is offline, store and deliver later. Presence (online/last seen) is a separate service with TTLs. Delivery receipts are extra events. Do not design full E2E encryption unless asked. The bottleneck is millions of open connections, not SQL for every message fan-out.
Requirements
- Send/receive
- Online/offline
- Last seen optional
Components
- Gateway (WebSocket)
- Message store
- Presence service
Data flow. Client keeps a long-lived connection. Messages queued if recipient is offline.
Scaling. Sticky sessions or a connection registry. Don't design end-to-end encryption unless asked.
YouTube
Upload, process, playback. CDN is the star.
Video HLD is upload to object storage, async transcoding into renditions, then playback almost entirely from a CDN. Metadata (title, owner) lives in a DB; bytes do not. Thumbnails are another derived asset. Almost no watch traffic should hit origin. Upload is a write path with a queue; watch is a cache path. That split is the design.
Requirements
- Upload
- Watch
- Thumbnails
Components
- Upload API
- Object storage
- Transcoding workers
- CDN
Data flow. Upload → store original → workers emit renditions → CDN serves playback.
Scaling. Almost all watch traffic should never hit origin.
More reading
Notification System
Events in, notifications out. Email/push/in-app. Retries.
A notification system takes domain events (order shipped) and fans out to channels (email, push, in-app) according to user preferences. The API enqueues; workers call providers; failures retry then dead-letter. Idempotency avoids double SMS. The queue absorbs spikes so checkout does not wait on Gmail. Scale workers independently of the request path.
Requirements
- Fan-out to channels
- User preferences
- At-least-once with idempotency
Components
- API
- Queue
- Workers per channel
- Preference store
Data flow. Event → queue → worker → provider. Failed jobs retry then dead-letter.
Scaling. Queue absorbs spikes. Workers scale independently of the API.
More reading
More reading
Books and platforms for this phase. Read the topics above first.