System Design Arena

System Design: URL Shortener (like Bitly)

Walk through the URL shortener design the same way you would in an interview: clarify the ask, estimate scale, define APIs, and narrate the architecture before touching code.

Functional RequirementsNon-Functional RequirementsAPI Endpoints

Case Study

URL Shortener

Same conversation, diagrams, and wrap-up you expectβ€”now framed with clearer scaffolding and iconography.

Prompt

Design a URL shortener that marketing teams can rely on during large campaigns. Support vanity links, expiration, analytics, and absolute reliability for redirects.

Interview snapshot

  • β€’ Topic: URL Shortener
  • β€’ Expected depth: 45 - 60 minutes
  • β€’ Focus areas: APIs, scale estimation, resilient architecture
  • β€’ Wrap-up: risk, monitoring, disaster recovery

Key takeaways

  • β€’ Shorten long URLs with optional custom alias and TTL.
  • β€’ Availability target 99.99% (redirect is business critical).
  • β€’ POST /api/links { originalUrl, alias?, expiresAt? } - validates URL, rate limits per user, persists metadata, publishes analytics event.

πŸŽ™οΈ Interview mode

Practice this like a real interview

Don't read the answer first. Work the framework against the bare prompt, then compare against what the interviewer expected at each step.

🎯 Start guided practice (free)

Scorecard

What the interviewer is evaluating

Requirements8/10
Scale estimation7/10
API design7/10
Architecture8/10
Trade-offs8/10
Failure handling6/10

Staff-level signal

Can the candidate identify that this is a read-dominated system (100:1) and let that single fact drive the cache, storage, and ID-generation decisions?

🧠 Staff engineer judgment

A URL shortener at 100M URLs fits in one Postgres instance. Don’t reach for Cassandra until you can say which number forces you off a single primary.

Calibration

The common (bad) answer

Client
  ↓
Load Balancer
  ↓
API Server
  ↓
Database

❌ Why this scores poorly: Technically correct and completely undifferentiated β€” it works for any CRUD app and shows zero system-design depth.

βœ“ What a strong answer adds

  • State the read/write ratio first β€” redirects dwarf creations, so the cache is the real system.
  • Explain short-code generation: counter + base62 vs random, and the collision story.
  • Say where consistency matters (custom aliases) and where it does not (click counts).
  • Give the redirect a latency budget and show how cache + CDN meet it.
  • Only then talk about database scaling β€” and defend why a single primary might be enough.

Build it up

Step-by-Step Walkthrough

Most candidates jump straight to the final diagram. Interviewers want to watch it evolve β€” start naive, find the bottleneck out loud, fix it, repeat. Here is the same journey, stage by stage.

1

Start embarrassingly simple

One web server, one relational database, two endpoints. POST /links hashes the long URL into a short code and inserts a row; GET /{code} looks the row up and returns a 302 redirect.

This genuinely works β€” and saying so is a strength, not a weakness. It gives you a baseline to measure every later decision against.

?Say the numbers before scaling anything

100M new links a month is only ~40 writes/sec. A single Postgres instance yawns at that. The read path is the real story β€” a popular link can pull thousands of redirects per second on its own.

2

The first crack: how do you generate the short code?

Hashing the URL (MD5, take 7 chars) collides eventually, and two users shortening the same URL now share analytics. Auto-increment IDs leak your total link count and create a single point of coordination.

The interview-winning move: a dedicated ID service issuing unique 64-bit IDs (Snowflake-style), Base62-encoded into a 7-character code. No collisions by construction, no coordination on the hot path.

3

Reads outnumber writes ~100:1 β€” cache the redirect

Every redirect that touches the database pays a disk-adjacent latency price and burns connections. A Redis cache keyed code β†’ long URL turns the overwhelming majority of redirects into a sub-millisecond memory lookup.

Links are immutable after creation, which makes this the friendliest caching problem imaginable: no invalidation story to defend beyond TTL + delete-on-expiry.

!The follow-up they will ask

What is your hit rate assumption? Link traffic is heavily skewed β€” a small fraction of links absorbs most clicks β€” so even a modest cache holds the hot set. Say β€œZipf-distributed” and mean it.

4

Go global: push redirects to the edge

A user in Singapore should not cross an ocean for a 302. Terminate at edge POPs (CloudFront/Fastly), where the redirect can be answered from POP-local cache; origin only sees the misses.

This is also where rate limiting and abuse filtering live β€” malicious shorteners are a real operational problem, and interviewers reward you for raising it unprompted.

5

Analytics without slowing the redirect

Click counting must never sit on the redirect path. Fire an event into Kafka after the 302 is already on the wire; a consumer aggregates into an analytics store that owners query.

This buys you exactly the trade interviewers want named: the redirect stays fast, and click counts become eventually consistent β€” off by seconds, which nobody dashboarding clicks will ever notice.

?The closing move

End by re-stating the guarantees: sub-100ms redirects from POP cache, zero collision codes, analytics eventually consistent by design. A crisp summary of trade-offs is what separates a hire from a lean hire.

Blueprint

Architecture Diagram

The narrated components above, laid out as an actual flow β€” so you can see how a request moves through the system, not just read a list of pieces.

Read Path β€” GET /r/{hash}

1

Client

requests short URL

2

Edge POP

Cloudflare / Fastly

3

Redis

hash β†’ URL

4

Aurora / Dynamo

cache-miss fallback

Cache hit returns a 302 in under 100ms without ever reaching the origin.

Write Path β€” POST /api/links

1

API Gateway

validate + rate limit

2

ID Generator

Snowflake β†’ Base62

3

Metadata Store

persist link row

4

Kafka

publish click topic

Analytics Pipeline

1

Kafka

click events

2

Flink

stream aggregation

3

BigQuery

owner dashboards

Flow Diagram

How the API maps to this flow

  1. 1POST /api/links { originalUrl, alias?, expiresAt? }β€” validates URL, rate limits per user, persists metadata, publishes analytics event.
  2. 2GET /r/{hash}β€” edge function that resolves hash, increments click counter async, returns 302.
  3. 3PATCH /api/links/{hash}β€” owner updates TTL, destination, status (active|paused).
  4. 4GET /api/links/{hash}/statsβ€” aggregates total clicks, referrers, geo (reads from analytics store).

?But why does this actually hold up at scale?

200K redirects/s -> 18B/day. With 95% cache hit, DB sees ~10K/s.

Video walkthrough

Beginner System Design Interview: Design Bitly

Hello Interview Β· ex-Meta staff engineer Β· 59 min

A full hour of the real thing β€” a staff engineer running the URL-shortener question end to end, including the follow-ups.

Work the prompt yourself first β€” guided practice grades your own answer step by step. Watching someone else design it feels like progress and teaches you far less.

Interview flow

Dialogue timeline

Interviewer

Give me the quick pitch for what this service must do.

Candidate

Users post a long URL and get back a short hash. Anyone visiting the hash must be redirected within 100 ms, and owners can manage TTLs plus analytics.

Interviewer

How do you size this?

Candidate

Assume 1M new links per day (~11 writes/s) but 200K redirects/s at peak events, so reads dwarf writes. Each metadata row ~500 bytes -> ~500 MB/day into storage.

Interviewer

Walk me through the critical APIs and data model.

Candidate

POST /api/links creates hashes, GET /r/{hash} handles redirects, PATCH toggles TTL/destination, and GET stats pulls analytics. Data model centers on a `links` table keyed by hash.

Interviewer

Scaling story?

Candidate

Edge cache first, then Redis (hash -> original URL), finally replicated SQL/NoSQL store. Hashes use Snowflake IDs encoded Base62 to avoid collisions. Analytics events stream via Kafka into a warehouse.

Scoping

Requirements & trade-offs

Functional Requirements

  • β€”Shorten long URLs with optional custom alias and TTL.
  • β€”Redirect within 100 ms even during regional launches.
  • β€”Expose owner controls: pause link, change destination, view click totals.
  • β€”Provide abuse-report workflow for malicious links.

Non-Functional Requirements

  • β€”Availability target 99.99% (redirect is business critical).
  • β€”Hot links must survive regional outages via multi-region replicas.
  • β€”All writes idempotent; collisions prevented via Snowflake-style IDs.
  • β€”DR: RPO < 5 min, RTO < 15 min using async replication.

Blueprint

Architecture modules

Module 1

API Endpoints

  • β€’POST /api/links { originalUrl, alias?, expiresAt? } - validates URL, rate limits per user, persists metadata, publishes analytics event.
  • β€’GET /r/{hash} - edge function that resolves hash, increments click counter async, returns 302.
  • β€’PATCH /api/links/{hash} - owner updates TTL, destination, status (active|paused).
  • β€’GET /api/links/{hash}/stats - aggregates total clicks, referrers, geo (reads from analytics store).

Module 2

Back-of-the-Envelope

  • β€’200K redirects/s -> 18B/day. With 95% cache hit, DB sees ~10K/s.
  • β€’Analytics log: 200K events/s * 150 bytes ~ 2.6 TB/day -> compress + tier to cold storage after 30 days.
  • β€’Redis cluster sized for 600K ops/s (3 replicas).
  • β€’Base62 7-char hash -> 3.5e12 combos, enough for decades.

Module 3

System Diagram Notes

  • β€’Edge POP (Cloudflare/Fastly) handles GET /r/{hash}; cache miss hits regional API tier.
  • β€’Write path: API GW -> app service -> ID generator -> metadata store (Aurora/Dynamo) -> publish click topic.
  • β€’Read path: Edge cache -> Redis -> DB fallback. Redirect worker refreshes caches on metadata change.
  • β€’Analytics: Kafka -> Flink -> BigQuery for dashboards.

Module 4

Design Playbook

  • β€’Clarify SLA, hash format, and allowed custom aliases.
  • β€’Explain collision avoidance + rate limiting to stop spam.
  • β€’Call out cache-invalidation strategy when destination changes.
  • β€’Discuss monitoring: synthetic redirects, latency heatmaps, analytics lag alerts.
  • β€’Conclude with compliance: GDPR deletion, audit logging, secure secret rotation.