01

Requirements & scope

Problem Statement & Requirements

Functional Requirements

  • Given a long URL, generate a short, unique alias (e.g., https://short.ly/abc123)
  • When a user hits the short URL, redirect them to the original long URL (HTTP 301/302)
  • Users can optionally pick a custom short alias
  • Links can have an expiration time (TTL)
  • Analytics: track click count, referrer, geo, device (optional but expected in interviews)

Non-Functional Requirements

  • High availability — redirections must never go down
  • Low latency — redirect in < 10 ms (p99)
  • URL should not be guessable — no sequential IDs
  • Consistency — same long URL can map to multiple short URLs (user-specific), but a short URL must always resolve to exactly one long URL
  • Durability — once created, a short link must survive infrastructure failures

Out of Scope

  • User authentication/accounts (simplify for interview)
  • Link editing after creation
  • Bulk URL shortening API
02

Scale estimations

Scale Estimations

Traffic

Metric Value
New URLs created / day 100M (write-heavy assumption)
Reads (redirections) / day 10B (read:write = 100:1)
Reads / second ~116K RPS
Writes / second ~1,160 RPS
Peak reads / second ~350K RPS (3x average)

Storage

Metric Value
Average URL length 500 bytes
Metadata per URL 100 bytes (created_at, expires_at, user_id, click_count)
Short key 7 bytes
Total per record ~607 bytes → round to 1 KB
Records over 5 years 100M/day × 365 × 5 = 182.5B records
Storage over 5 years 182.5B × 1 KB = ~182.5 TB

Bandwidth

Metric Value
Incoming (writes) 1,160 × 1 KB = ~1.16 MB/s
Outgoing (reads) 116K × 1 KB = ~116 MB/s

Cache

  • 80/20 rule: 20% of URLs generate 80% of traffic
  • Cache 20% of daily read requests: 10B × 0.2 × 1 KB = ~2 TB cache
  • Fits in a Redis cluster
03

Layered architecture

High-Level Architecture

High-Level ArchitectureExcalidraw diagram · editable shapes · reveal step by stepExplore

Component Breakdown

Component BreakdownExcalidraw diagram · editable shapes · reveal step by stepExplore
04

API & contracts

API Design

Create Short URL

EXAMPLE
POST /api/v1/urls
Content-Type: application/json

Request:
{
  "long_url": "https://www.example.com/very/long/path?q=something",
  "custom_alias": "my-brand",     // optional
  "expires_at": "2026-12-31T00:00:00Z"  // optional
}

Response (201 Created):
{
  "short_url": "https://short.ly/abc123",
  "short_key": "abc123",
  "long_url": "https://www.example.com/very/long/path?q=something",
  "expires_at": "2026-12-31T00:00:00Z",
  "created_at": "2026-04-03T10:00:00Z"
}

Redirect (Read)

EXAMPLE
GET /:short_key
→ HTTP 301 (permanent) or 302 (temporary) redirect to long_url

301 vs 302 Tradeoff:
  - 301: Browser caches the redirect → less load on our servers,
         but we lose analytics visibility (browser skips us on repeat visits)
  - 302: Browser always hits us → more load but full analytics tracking
  → Use 302 if analytics matter; 301 if pure performance is the goal

Delete URL

EXAMPLE
DELETE /api/v1/urls/:short_key
→ 204 No Content
05

Data model

Data Model

URL Table (Primary)

URL Table (Primary)Excalidraw diagram · editable shapes · reveal step by stepExplore

Database Choice

Option Pros Cons
DynamoDB Managed, auto-scaling, single-digit ms latency, partition by short_key No joins (not needed here), cost at extreme scale
Cassandra Linear scalability, tunable consistency, great for write-heavy Operational complexity, eventual consistency
MySQL + sharding ACID, familiar, strong consistency Manual sharding, operational burden at scale

Recommendation: DynamoDB or Cassandra — this is a key-value lookup workload with high write throughput. No joins, no complex queries.

06

Core design decisions

Key Generation — The Core Design Decision

This is the most important part of the design. How do we generate a short, unique, non-guessable key?

Option 1: Hash the Long URL (MD5/SHA-256 + Base62 Truncation)

Option 1: Hash the Long URL (MD5/SHA-256 + Base62 Truncation)Excalidraw diagram · editable shapes · reveal step by stepExplore

Key space: Base62 with 7 characters = 62^7 = 3.5 trillion unique keys

Collision handling:

Option 1: Hash the Long URL (MD5/SHA-256 + Base62 Truncation)Excalidraw diagram · editable shapes · reveal step by stepExplore
Pros Cons
Deterministic — same URL → same hash Collisions require DB lookup + retry
Simple to implement Same long URL → same short URL (no per-user links)
No coordination needed MD5 is not cryptographically secure (fine for this use)

Option 2: Pre-Generated Key Pool (Key Generation Service — KGS)

Option 2: Pre-Generated Key Pool (Key Generation Service — KGS)Excalidraw diagram · editable shapes · reveal step by stepExplore

How it works:

  1. Offline job generates random 7-char Base62 keys, stores in unused_keys table
  2. App server requests a batch (e.g., 1,000 keys) at startup, caches them in memory
  3. On URL creation, pop a key from the in-memory batch
  4. When batch runs low (< 200), fetch another batch asynchronously
  5. Mark keys as "used" in the DB atomically

Concurrency safety:

  • Use a two-table approach: unused_keys and used_keys
  • Move key from unused → used in a single transaction
  • If an app server crashes, its unused in-memory keys are "lost" — acceptable given the 3.5T key space
Pros Cons
Zero collision — guaranteed unique Need a separate KGS service
O(1) key generation — no DB check needed Pre-generated keys take storage
Fast — just pop from in-memory cache Wasted keys if server crashes (negligible)
No hash computation Single point of failure → need standby KGS

Option 3: Counter-Based with Encoding (Snowflake-style)

Option 3: Counter-Based with Encoding (Snowflake-style)Excalidraw diagram · editable shapes · reveal step by stepExplore
Pros Cons
Zero collision — ranges don't overlap Sequential/predictable (guessable)
No DB lookup for uniqueness check Requires Zookeeper or similar coordination
Simple, efficient Range exhaustion needs re-allocation

Recommendation

Use Option 2 (KGS) for production systems:

  • No collisions, no coordination overhead per request
  • O(1) amortized key generation
  • Easy to make non-guessable (random keys, not sequential)
  • Can be made highly available with standby KGS replicas
07

Request flows

Detailed Flow Diagrams

Write Flow (URL Creation)

Write Flow (URL Creation)Excalidraw diagram · editable shapes · reveal step by stepExplore

Read Flow (Redirection)

Read Flow (Redirection)Excalidraw diagram · editable shapes · reveal step by stepExplore

Analytics Flow (Async)

Analytics Flow (Async)Excalidraw diagram · editable shapes · reveal step by stepExplore
08

Performance & caching

Caching Strategy

Cache Architecture

Cache ArchitectureExcalidraw diagram · editable shapes · reveal step by stepExplore

Caching Patterns

Pattern How It Works When to Use
Cache-Aside App checks cache → miss → query DB → populate cache ✅ Best for this use case
Write-Through Write to cache + DB on creation Wastes cache on URLs that may never be read
Write-Behind Write to cache, async flush to DB Risky — URL could be lost if cache crashes

Recommendation: Cache-Aside with Write-on-Create for popular URLs

  • On URL creation: don't populate cache (most URLs are rarely accessed)
  • On first read: query DB, populate cache
  • Exception: if we detect a URL going viral (high click rate), proactively cache it

Cache Warming

  • Preload the top 1% most-accessed URLs into cache on service startup
  • Use a daily batch job scanning analytics to identify hot URLs
09

Advanced design

Database Sharding & Partitioning

Sharding Strategy

Sharding StrategyExcalidraw diagram · editable shapes · reveal step by stepExplore

Recommendation: Consistent Hashing on short_key

  • Even distribution
  • Adding/removing DB nodes only requires moving ~1/N of the keys
  • Use virtual nodes (vnodes) for better balance — 150-200 vnodes per physical node

Replication

ReplicationExcalidraw diagram · editable shapes · reveal step by stepExplore
10

Edge cases

Handling Edge Cases

Duplicate Long URLs

  • Same user, same URL: Return existing short URL (dedup query on long_url + user_id index)
  • Different users, same URL: Generate separate short URLs (user isolation)

Expired URLs

  • Lazy deletion: On read, check expires_at → if expired, return 410 Gone
  • Background cleanup: Cron job deletes expired records, recycles keys back to KGS

Custom Aliases

  • Validate: 3-16 chars, alphanumeric + hyphens only
  • Check uniqueness in DB
  • Reserve a set of blacklisted words (profanity, reserved paths like /api, /admin)

Hot URLs (Viral Links)

  • A single URL getting millions of hits/second
  • Solution: Cache replication — replicate hot keys across multiple cache shards
  • Application-level: detect hot keys (e.g., > 1000 RPS) and fan out reads across replicas
11

Tradeoffs

Tradeoffs & Design Decisions Summary

Decision Option A Option B Chosen Why
Key Generation Hash + collision check Pre-generated KGS KGS Zero collisions, O(1), non-guessable
Redirect Code 301 (permanent) 302 (temporary) 302 Preserves analytics; 301 loses repeat visit tracking
Database SQL (MySQL/Postgres) NoSQL (DynamoDB/Cassandra) NoSQL Key-value access pattern, high write throughput, linear scaling
Caching Write-through Cache-aside Cache-aside Most URLs are rarely accessed; avoid wasting cache on cold URLs
Sharding Range-based Consistent hashing Consistent hashing Even distribution, graceful scaling
ID Length 6 chars (56B keys) 7 chars (3.5T keys) 7 chars Room for 5+ years at 100M URLs/day
Analytics Synchronous in request path Async via Kafka Async Kafka Don't add latency to the redirect hot path
12

Reliability & fault tolerance

Reliability & Fault Tolerance

Single Points of Failure & Mitigations

Single Points of Failure & MitigationsExcalidraw diagram · editable shapes · reveal step by stepExplore

Graceful Degradation

  • If cache is down → serve directly from DB (higher latency, still functional)
  • If KGS is down → app servers use remaining in-memory keys; alert for manual intervention
  • If analytics pipeline is down → buffer events locally, replay later
13

Production architecture

Full System Architecture (Production-Grade)

Full System Architecture (Production-Grade)Excalidraw diagram · editable shapes · reveal step by stepExplore
14

Further exploration

Workshop note · added for the website’s common reading format

Rebuild URL Shortener from memory, then change one assumption: ten times more traffic, a new region, or a stricter consistency requirement. Which component must change first—and which does not?

Compare URL service with the same boundary in a related design. Write down one alternative you rejected, what it would simplify, and when you would choose it instead. Follow the source link at the end of this article to explore the original document.

15

Interview playbook

Interview Tips

  1. Start with requirements clarification — Don't jump into design. Ask about scale, custom aliases, analytics, expiration.

  2. Lead with the key generation discussion — This is the core algorithmic challenge. Discuss all three approaches, tradeoffs, and recommend KGS.

  3. Separate read and write paths — They have very different scale (100:1 ratio). This shows you think about system characteristics.

  4. Don't forget caching — With 100:1 read:write ratio, caching is critical. Discuss eviction, warming, and hot key handling.

  5. Mention 301 vs 302 — This is a subtle but important tradeoff that shows depth.

  6. Discuss cleanup of expired URLs — Shows you think about long-term system health and operational concerns.

  7. End with reliability — Walk through failure scenarios. "What happens if X goes down?" for each component.