System Design — High-Level Design (HLD) from Basics to Case Studies
In short: HLD shows the architecture of the whole system: components (clients, load balancers, services, databases, caches, queues, CDN), how they interact, data flow, scaling and failure handling.
- LLD details the inside of components: classes, interfaces, methods, design patterns, database tables and APIs.
- HLD answers "what pieces and how they connect"; LLD answers "how each piece is built".
In short: Vertical: give one machine more CPU/RAM — simple, no code change, but limited and a single point of failure.
- Horizontal: add more machines behind a load balancer — almost unlimited and fault tolerant, but services must be stateless and data must be replicated/partitioned.
- Typical path: scale up first, then out.
In short: Storing frequently read data in faster storage (memory/Redis/CDN) to reduce latency and database load.
- Cache-aside: on read, check the cache;
- on miss, read from the database and put the result in the cache with a TTL;
- on write, update the database and delete (or update) the cache key.
- Watch for stale data, stampedes and hot keys.
In short: Distributes requests across multiple servers to increase capacity and availability, performs health checks to stop sending traffic to failed servers, and can terminate TLS and route by URL (L7).
- Algorithms: round robin, least connections, IP hash/consistent hashing, weighted.
In short: In a distributed system, during a network partition you can't have both strong consistency and full availability.
- CP systems reject or delay requests to avoid stale data (bank balances, configuration stores like etcd);
- AP systems keep answering and reconcile later (social feeds, DNS, carts in Dynamo-style stores).
- When there is no partition, the trade-off is latency vs consistency (PACELC).
In short: Start from the data and access patterns.
- SQL for structured, related data needing transactions, joins and constraints (orders, payments).
- NoSQL when you need massive horizontal scale for simple key-based access, flexible schemas, very high write throughput, or special models (documents, graphs, time-series).
- Many systems combine them: SQL as the source of truth, Redis cache, search index, blob storage.
In short: Splitting a dataset horizontally across multiple database nodes so each stores a subset, increasing capacity and throughput.
- A good shard key has high cardinality, spreads reads and writes evenly (no hot shard) and matches the main query pattern so most queries hit one shard (e.g. customer id for orders).
- Use consistent hashing or a directory to allow adding shards with minimal data movement.
In short: Nodes and keys are placed on a hash ring; each key belongs to the next node clockwise.
- When a node is added or removed, only the keys between it and its neighbour move (~1/N of keys), unlike
hash % Nwhere almost all keys move. - Virtual nodes (many points per server) spread load evenly.
- Used in distributed caches and databases (Dynamo, Cassandra) and load balancers.
In short: To decouple services (the producer doesn't wait for or depend on the consumer being up), absorb traffic spikes (load levelling), process work asynchronously (emails, video encoding), retry failures with dead-letter queues, and fan out events to multiple subscribers.
- Consumers must be idempotent because delivery is at-least-once.
In short: Synchronous: the write is acknowledged only after replicas confirm — no data loss on failover but higher latency and reduced availability if a replica is slow.
- Asynchronous: acknowledge after the primary writes — fast, but replicas lag (stale reads) and recent writes can be lost if the primary fails.
- Many systems use sync within a region and async across regions.
In short: Pick an algorithm (token bucket for bursts, sliding window for smoothness), a key (user id, API key, IP), and limits per endpoint.
- Store counters in Redis with atomic operations and TTLs so all API instances share them;
- return 429 with Retry-After;
- put it in the API gateway or middleware;
- allow configuration per plan;
- fail open or closed by policy if Redis is down.
In short: Write API creates a short code (base62 of a unique id or random 7 chars with collision check) and stores code → long URL in a key-value store or SQL with a unique index;
- read path
GET /{code}checks Redis, then the store, and returns a 301/302 redirect. - Reads dominate (100:1) → cache and CDN/edge.
- Analytics: send click events to a queue/stream and aggregate asynchronously.
- Scale: shard by code; custom aliases, expiry and abuse checks as extras.
In short: Fan-out on write pushes post ids into followers' precomputed feeds (fast reads, expensive for celebrities);
- fan-out on read builds the feed at read time (cheap writes, slow reads).
- Use a hybrid: push for normal users, pull and merge for celebrity accounts.
- Store feeds in Redis lists, posts in a sharded store, media via CDN, use cursor pagination, and rank asynchronously.
In short: Recovery Point Objective: the maximum acceptable data loss measured in time (e.g. 5 minutes → you need replication or log backups at least every 5 minutes).
- Recovery Time Objective: the maximum acceptable downtime to restore service (e.g. 1 hour → automated failover or a tested runbook).
- Lower values cost more: synchronous replication, hot standby regions.
In short: Measure the top queries first;
- add/fix indexes and remove N+1 queries;
- add caching (in-memory/Redis) for hot data and HTTP caching/CDN where possible;
- route reads to read replicas;
- reduce payloads with projection and paging;
- scale the database up as a quick fix;
- finally partition/shard if data or write volume demands it.
- Monitor after each step.
In short: Load test beforehand;
- pre-scale and warm caches;
- serve static content from the CDN;
- put a virtual waiting room/queue in front of checkout;
- rate limit per user;
- make inventory reservation atomic (conditional update) to prevent overselling;
- move non-critical work (emails, analytics) to queues;
- use feature flags to switch off expensive features;
- monitor dashboards and have a rollback plan.
In short: Measure where time goes (network latency ≈ 150 ms per round trip).
- Put static assets and cacheable responses on a CDN;
- reduce round trips (bundle requests, HTTP/2/3);
- deploy the stateless API in a European region behind global routing (Azure Front Door/Traffic Manager);
- add read replicas in Europe for read-heavy data, keep writes in the primary region or use multi-region databases (Cosmos DB, Azure SQL geo-replication) with clear consistency choices;
- consider data-residency (GDPR) requirements.