After this lesson, you will be able to: Walk through three canonical system-design problems (URL shortener, notification system, Twitter feed) using the building blocks from earlier lessons.
This is the lesson the interview actually tests. We'll walk through three classic prompts end-to-end, naming the decisions and tradeoffs at each step.
Functional: given a long URL, return a short URL; redirect short → long; track click counts. Non-functional: 100M URLs total; 100:1 read:write; 100ms p99 redirect latency; URL never expires. Design: PostgreSQL table (id PK, short_code UNIQUE, long_url, created_at, clicks). Short code = base62 encoding of id, or a random 7-char string. Read path: GET /<code> → look up in DB (or Redis cache) → 301 redirect. Cache hits ~95% because hot links are reused. Write path: POST / → INSERT → return short code. Scale: read replicas handle the 100:1 reads. Cache hot codes in Redis with 24h TTL. CDN cache the 301 with Cache-Control: public, max-age=86400. Click count: don't UPDATE on every redirect (kills write throughput). Increment in Redis; flush to DB hourly. Failure modes: collision on random codes (retry; or use base62 of id, which never collides). Hot link DDoS (Cloudflare rate-limit per IP).
Functional: trigger a notification when an event occurs; deliver via email + push + in-app; respect user preferences. Non-functional: 10M users; 10K events/sec peak; 1s end-to-end delivery for in-app; 30s for email. Design: event bus (Kafka or SQS) receives all events. A notification service consumes the bus, looks up user preferences in DB, fans out to per-channel queues (email, push, in-app). Workers per channel deliver. Per-channel queues let you scale the slow channels (email via SendGrid) without slowing the fast ones (in-app via WebSocket). User preferences: cached in Redis with explicit invalidation on update. Idempotency: each event has a unique ID. The notification service writes (event_id, user_id, channel) before delivering; if it sees the same combo twice, it skips. Failure modes: a delivery service is down → messages pile in its queue → other channels keep working (decoupling pays off). Email API throttles us → exponential backoff + DLQ. Observability: per-channel delivery rate, latency p99, retry rate. Without these, you'll only know it's broken when users complain.
Functional: user opens app → sees a feed of tweets from people they follow, newest first. Post a tweet. Follow / unfollow users. Non-functional: 100M DAU; 5,000 tweets/sec on average, 100K/sec at peak; 200ms p99 timeline load; 100:1 read:write at the timeline. The core tradeoff: fan-out on write (push) vs fan-out on read (pull). Pull (compute timeline on read): on timeline request, query 'all tweets where author IN (people I follow) ORDER BY time'. Simple but slow at scale (joins blow up). Push (precompute timelines): when a user tweets, write a row into the inbox of every follower. Reads are O(1) (just read the inbox). Writes amplify (a celebrity with 100M followers triggers 100M writes per tweet). Hybrid (production reality): push for most users (writes are cheap); pull for celebrities (writes would be too expensive); merge the two at read time. Storage: tweets in a sharded database (by user_id); inboxes in Redis sorted sets (capped at the last ~1000 tweets per user). Cache: timelines for active users in Redis; recompute lazily for inactive users. Failure modes: a celebrity tweets → backpressure on their fan-out queue → use the pull path until backlog clears. Timeline corruption → recompute from tweet table.
File storage service (think S3): clients should not stream large files through your API server. Issue a presigned URL so the client uploads directly to object storage; store only metadata (key, size, owner, content-type) in your database. Serve downloads through a CDN in front of the bucket. Key decisions: chunked/multipart upload for large files, deduplication by content hash, and lifecycle rules to move cold objects to cheaper tiers. Search autocomplete: the suggestion box that completes a query as the user types, with sub-100ms latency. Back it with a trie (prefix tree) of popular queries, or a search engine like Elasticsearch/OpenSearch with edge-n-gram indexing. Precompute the top-k completions per prefix and cache them in Redis, since the same prefixes are typed constantly. Update popularity counts asynchronously from a log stream, not on the hot path. This connects directly to the trie data structure from the Computer Science Fundamentals track.
Designing for 1B users when the problem is 100K. Pick the simplest design that meets the requirements, then state what would change at higher scale. Picking technologies without justification. Mongo because you've used it isn't a reason; the data is relational, so Postgres. Skipping the failure modes. Every interviewer asks. Have an answer ready. Treating it as a quiz. The interview is a CONVERSATION; ask the interviewer questions back. Memorizing answers from blog posts. The interviewer can tell. Practice the THINKING, not the answer.
Pick the most damaging.
Sign in and purchase access to unlock this lesson.