Designing a System (the Whiteboard Skill)
Stage 3 ยท Architecture & System Design ยท B.U.I.L.D. letter: I
You can ship a front end in an afternoon with AI. Designing the system behind it โ one that holds up under real traffic, real failures, and real teammates โ is a different skill. It is a learnable, repeatable process, and this lesson hands it to you in seven steps.
โ ๏ธ The vibe trap
The most expensive mistake in software is starting to build before you understand what you are building. When a prompt like "design a URL shortener" lands in your lap it is tempting to open a code editor and start wiring routes together โ but without requirements you will build the wrong thing at the wrong scale with the wrong data model. The framework below is the antidote: spend the first twenty minutes asking questions and sketching, and you will write better code faster with far fewer dead ends.
๐ Step 1 โ Clarify Requirements & Scope
Before touching a keyboard, interrogate the prompt. Every system design has two layers of requirements.
Functional requirements โ what the system must do:
- Users can submit a long URL and receive a short code (e.g.
hyv.cc/xK3m) - Anyone with the short code is redirected to the original URL
- (Optional) Link owners can see a click count
Non-functional requirements โ how the system must behave:
- Read-heavy (redirects) vs. write-heavy? โ overwhelmingly reads
- Availability vs. consistency? โ availability wins; a stale redirect beats a 503
- Latency target? โ < 50 ms p99 for redirects
- Durability? โ links should never silently vanish
Scope decisions (write these down explicitly):
- Are analytics in scope? โ no for v1
- Custom slugs? โ no for v1
- Link expiry? โ no for v1
Scoping out features is as important as scoping them in. An interviewer โ or a stakeholder โ respects a candidate who defines boundaries early.
๐ Step 2 โ Estimate Scale (Back-of-Envelope)
Rough numbers unlock the entire rest of the design. You do not need precision; you need an order of magnitude.
URL Shortener โ back-of-envelope estimate
==========================================
Daily active users: 10 million
New links created/day: 10 million / 100 = 100 K writes/day
Redirects/day (100:1 r/w): 10 million reads/day
Writes/sec (peak 2รavg): 100 K / 86 400 โ 1.2 โ ~3 writes/sec peak
Reads/sec (peak 2รavg): 10 M / 86 400 โ 116 โ ~250 reads/sec peak
Storage per link row: ~500 bytes (URL + code + metadata)
Storage/year: 100 K ร 365 ร 500 B โ 18 GB/year (tiny)
Bandwidth (redirect response โ 0.5 KB):
250 reads/sec ร 0.5 KB = 125 KB/sec outbound (trivial)
Conclusion: read-heavy, low write volume, storage is not the constraint.
Cache the hot links; a single DB can handle writes for years.
This estimate tells you three things that directly shape your design:
- Cache is the highest-value investment (250 reads/sec, same few popular links hit repeatedly).
- Write throughput is low โ no need for write sharding on day one.
- Storage is tiny โ no special object store needed for link data.
Mental model: Every number you put on the whiteboard is a lever. When you ask "what if traffic 10ร?" you can re-run the math in seconds and say "we'd need to distribute the cache" โ that is the answer the interviewer wants.
Common mistake: Skipping this step and jumping to "we'll use microservices" with no idea whether that is warranted. Estimates justify (or reject) every architectural choice that follows.
๐ Step 3 โ Define the API
The API is the contract between your system and the outside world. Sketch it before you design internals.
URL Shortener โ API sketch
==========================
POST /links
Body: { "long_url": "https://example.com/very/long/path" }
Returns: { "short_code": "xK3m", "short_url": "https://hyv.cc/xK3m" }
Status: 201 Created
GET /{short_code}
Returns: 301 Redirect โ Location: <long_url>
Status: 301 (cached by browsers) or 302 (not cached, better for analytics)
(Out of scope for v1)
GET /links/{short_code}/stats
Returns: { "clicks": 42 }
Two endpoints. That is it. Defining the API first forces you to think about what data flows in and out, which in turn shapes your data model.
Common mistake: Designing the database first and then bolting on an API that reflects the DB schema rather than the caller's needs. API-first keeps you honest about what actually matters to users.
๐๏ธ Step 4 โ Data Model
With the API defined, the data model almost writes itself.
URL Shortener โ data model
==========================
Table: links
โโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Column โ Type โ Notes โ
โโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ short_code โ VARCHAR(8) PK โ e.g. "xK3m" โ base62 chars โ
โ long_url โ TEXT NN โ original URL, up to ~2 KB โ
โ created_at โ TIMESTAMPTZ NN โ default now() โ
โ click_count โ BIGINT NN โ default 0 (v1 out of scope, โ
โ โ โ but cheap to add now) โ
โโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Index: links_short_code_idx ON links(short_code) โ PRIMARY KEY covers this
No FK constraints needed; table is self-contained.
short_code generation strategy:
Option A: Hash long_url (MD5), take first 6 chars base62 โ collision risk
Option B: Random 6โ8 base62 chars, check uniqueness before insert โ preferred
Option C: Auto-increment ID, encode to base62 โ simplest, no collision risk
Pick C for v1: INSERT โ get row ID โ encode ID โ UPDATE short_code.
The data model is small because the problem is small. Resist the urge to add tables for features you scoped out. You can always migrate later; you cannot un-complicate an over-engineered schema easily.
๐๏ธ Step 5 โ High-Level Architecture
Now draw the boxes. Every component should earn its place โ if you cannot explain why a box exists, remove it.
URL Shortener โ high-level architecture
========================================
Browser / Client
โ
โ POST /links (create)
โ GET /{code} (redirect)
โผ
โโโโโโโโโโโโโโโโโโโโโโโ
โ API Gateway / โ โ TLS termination, rate limiting, auth headers
โ Load Balancer โ
โโโโโโโโโโโโฌโโโโโโโโโโโ
โ
โโโโโโโโผโโโโโโโ
โ App Server โ โ stateless; 2โ4 replicas behind LB
โ (Node / โ
โ Go / etc) โ
โโโโฌโโโโโโโฌโโโโ
โ โ
โ โ read path (lookup short_code)
โ โผ
โ โโโโโโโโโโโโโโโโ
โ โ Redis Cache โ โ short_code โ long_url; TTL 24 h
โ โ (hot links) โ ~95% of reads never hit the DB
โ โโโโโโโโโโโโโโโโ
โ
โ write path (create link) + cache miss reads
โผ
โโโโโโโโโโโโโโโโโโโโโโโ
โ Postgres (primary) โ โ single node handles our write volume easily
โ โ read replica optional for analytics later
โโโโโโโโโโโโโโโโโโโโโโโ
Read path (redirect):
1. App receives GET /xK3m
2. Check Redis for key "xK3m"
3a. Cache HIT โ 301 to long_url (< 5 ms)
3b. Cache MISS โ query Postgres, populate cache, 301 to long_url
Write path (shorten):
1. App receives POST /links
2. Generate base62 short_code (encode auto-increment ID)
3. INSERT into Postgres โ get ID โ encode โ 201 response
4. Optionally pre-warm cache
No queue needed for v1 โ write volume is ~3/sec.
Mental model: Draw the client on the left, the database on the right, and fill in the middle. Each hop is a place where latency, failure, or cost can hide. Name every hop.
Common mistake: Drawing microservices for a system that has two endpoints and 3 writes/second. Microservices are a solution to organisational and scaling problems that this system does not yet have. Ship a single well-structured app first (see Lesson 7: Monolith vs. Services).
๐ Step 6 โ Drill Into Bottlenecks & Scale Them
Your back-of-envelope told you this system is read-heavy. Walk through each component and ask: "what breaks first if traffic 10ร or 100ร?"
Cache โ already the answer to read load. At 2 500 reads/sec (10ร) Redis still handles it trivially; Redis can sustain hundreds of thousands of ops/sec on a single node.
Database writes โ at 30 writes/sec (10ร) a single Postgres instance is comfortable. At 300 writes/sec you would look at connection pooling (PgBouncer) before considering sharding.
Short-code generation โ encoding the auto-increment PK is single-threaded through the primary DB. If write volume explodes, switch to a distributed ID generator (Twitter Snowflake pattern) and you remove the DB round-trip from the generation path.
App servers โ stateless, so horizontal scaling is trivial. Add replicas behind the load balancer.
Cache invalidation โ if a link is deleted or updated, you must evict the Redis key. Set a sensible TTL (24 h) so stale entries self-heal even if an explicit eviction fails.
Trade-offs to discuss explicitly:
- 301 (permanent redirect) vs. 302 (temporary): 301 is cached by browsers โ faster for repeat visitors, but you lose analytics visibility. 302 lets your server see every click.
- Availability vs. consistency: if Redis is down, fall back to Postgres (availability). If Postgres is down during a write, return 503 and tell the user to retry (no silent data loss).
- Short-code length: 6 base62 chars = 56 billion combinations. 7 chars = 3.5 trillion. Choose based on how many links you expect over the service lifetime.
๐ ๏ธ Your mission
Open the System Design Template resource (linked in your Institute dashboard). Work through it end-to-end for a system of your choice โ a pastebin, a ride-share dispatch service, a notification delivery system, or anything else you find interesting. Fill in every section:
- Functional & non-functional requirements, with explicit scope decisions
- Back-of-envelope estimate with at least three derived numbers
- API sketch (at minimum two endpoints)
- Data model table with column types and a note on the primary access pattern
- Architecture diagram in plain text showing at least: client, API layer, app server, cache, database
- Bottleneck analysis โ what breaks first and how you would scale it
- At least two named trade-offs with your reasoning
If you already have a side project in mind, use that. Real motivation makes the exercise ten times more valuable.
โ You're done whenโฆ
- You opened and used the System Design Template to structure your design
- You wrote a back-of-envelope estimate and used the numbers to justify at least one architectural decision
- Your architecture diagram shows the full request path from client to database and back
- You named at least two trade-offs and explained which side you chose and why
โก๏ธ Next: Scaling Fundamentals. Build It Right, Or Don't Build It At All. ๐๏ธ