1. Requirements Clarification

Before jumping into architecture, nail down requirements. Interviewers want to see you drive this conversation.

Functional Requirements

  • Given a long URL, generate a unique short URL (e.g. https://fb.in/aB3x9Z)
  • Redirect users from short URL to original long URL
  • Support custom aliases (e.g. fb.in/my-product-launch)
  • URL expiry — optional TTL per link
  • Click analytics — count clicks, geographic breakdown, referrer
  • User accounts — link ownership and management

Non-Functional Requirements

  • Scale: 100 million URLs created per day, 10:1 read-to-write ratio = 1 billion redirects/day
  • Redirect latency: <10ms at P99
  • High availability: 99.99% uptime (52 minutes downtime/year)
  • Storage: URLs kept for 5 years; ~500 bytes per record → 100M × 365 × 5 × 500B ≈ 91 TB
  • Eventual consistency acceptable for analytics; strong consistency required for URL creation (no duplicate short codes)

2. High-Level Architecture

  Client
    │
    ▼
┌─────────────┐     ┌─────────────────────────────────────────┐
│   CDN Edge  │────▶│         Load Balancer (L7)              │
│ (CloudFront)│     └────────────────┬────────────────────────┘
└─────────────┘                      │
                          ┌──────────┴──────────┐
                          │                     │
                   ┌──────▼──────┐     ┌────────▼───────┐
                   │  Redirect   │     │  Write Service  │
                   │  Service    │     │  (URL Creation) │
                   │  (read-only)│     └────────┬───────┘
                   └──────┬──────┘              │
                          │              ┌───────▼──────┐
                   ┌──────▼──────┐       │  ID Generator│
                   │  Redis Cache│       │  (Snowflake) │
                   │  (hot URLs) │       └───────┬──────┘
                   └──────┬──────┘              │
                          │              ┌───────▼──────┐
                   ┌──────▼──────────────▼──────────────┐
                   │           MySQL / PostgreSQL        │
                   │         (Primary + Read Replicas)   │
                   └────────────────────────────────────┘
                                    │
                             ┌──────▼──────┐
                             │    Kafka    │  ← async click events
                             └──────┬──────┘
                                    │
                             ┌──────▼──────┐
                             │  Analytics  │
                             │  Pipeline   │
                             └─────────────┘

3. ID Generation Strategies

The short code is the heart of the system. Getting ID generation right determines scalability and correctness.

StrategyExample OutputCollision RiskDistributed-SafeRecommendation
Auto-increment DB ID + Base62aB3x9ZNoneSingle writer onlyGood for small scale
Snowflake ID + Base623Kp7mNNoneYes (multiple writers)Best for large scale
MD5 hash, first 6 charsa3f9bcLow but non-zeroYesAvoid — needs collision handling
UUID truncated3f8a-bcVery lowYesLonger codes, harder to type
Random 6-char base62Xq8Z2mGrows with scaleYesAcceptable with collision check

Base62 Encoding Explained

Base62 uses digits 0–9, uppercase A–Z, and lowercase a–z — 62 characters total. It produces URL-safe codes without special characters. Here is the encoding algorithm:

Python — base62 encoder ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" def to_base62(num: int) -> str: if num == 0: return ALPHABET[0] result = [] while num: num, remainder = divmod(num, 62) result.append(ALPHABET[remainder]) return ''.join(reversed(result)) # Examples: # to_base62(1) → "1" # to_base62(1000000) → "4c92" # to_base62(999999999) → "15FTGf" (6 chars) # 62^6 = 56,800,235,584 unique codes from 6 characters

Interview Tip — Why Not MD5?

MD5 of "https://example.com" starts with "5d41…". Taking the first 6 hex characters gives 16^6 = 16.7M combinations — birthday paradox means collisions start appearing around 4,000 URLs. Base62 of a unique integer ID has zero collisions by design.

4. Database Schema

The schema is intentionally simple. The urls table is the single source of truth. We optimise read performance through indexing and caching rather than schema complexity.

SQL — URLs table CREATE TABLE urls ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, short_code VARCHAR(10) NOT NULL, long_url TEXT NOT NULL, user_id BIGINT UNSIGNED NULL, -- NULL = anonymous created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, expires_at DATETIME NULL, -- NULL = never expires click_count BIGINT UNSIGNED NOT NULL DEFAULT 0, is_active TINYINT(1) NOT NULL DEFAULT 1, PRIMARY KEY (id), UNIQUE KEY uk_short_code (short_code), KEY idx_user_id (user_id), KEY idx_expires_at (expires_at) ) ENGINE=InnoDB; CREATE TABLE click_events ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, short_code VARCHAR(10) NOT NULL, clicked_at DATETIME NOT NULL, ip_country CHAR(2) NULL, referrer VARCHAR(500) NULL, user_agent VARCHAR(500) NULL, PRIMARY KEY (id), KEY idx_short_code_time (short_code, clicked_at) ) ENGINE=InnoDB; -- Note: in production, click_events is partitioned by month -- or stored in a columnar store (ClickHouse) for analytics queries

5. Redirect Flow — Cache-First

The redirect path must be as fast as possible. Every millisecond adds up when you serve 11,500 redirects per second. The cache-first pattern keeps database load near zero for popular URLs.

User clicks short URL
        │
        ▼
  CDN Edge Cache?  ──YES──▶  301 Redirect (cached at edge, ~2ms)
        │NO
        ▼
  Redirect Server
        │
        ▼
  Redis Cache?  ──YES──▶  302 Redirect + async Kafka event (~5ms)
        │NO
        ▼
  Database Read  ──FOUND──▶  Populate Redis + Redirect (~15ms)
        │NOT FOUND
        ▼
  Return 404

Redis key structure: url:{short_code} → JSON with long_url, expires_at, user_id. TTL on Redis key matches URL expiry, or defaults to 24 hours for non-expiring URLs (with lazy refresh on access). A single Redis node handles 100,000+ ops/second — far above our 11,500 redirects/second requirement.

6. Write Path — URL Creation

URL creation is less frequent than reads (10:1 ratio) but requires strong consistency — two concurrent requests must not get the same short code.

  1. Client sends POST /api/shorten with long_url, optional custom_alias, optional expires_at
  2. Write Service validates the URL (regex + optional reachability check)
  3. If custom alias: check DB for availability, return 409 if taken
  4. Otherwise: request next ID from ID Generator (Snowflake service or DB auto-increment)
  5. Encode ID to base62 to get short_code
  6. Insert row into urls table (UNIQUE constraint on short_code prevents races)
  7. Return short URL to client

Distributed ID Generation with Snowflake

Twitter's Snowflake format: 41-bit timestamp + 10-bit machine ID + 12-bit sequence = 64-bit integer. Generates 4,096 unique IDs per millisecond per machine, sortable by time, and produces shorter base62 codes than UUID. Many teams use a dedicated ID service or database sequence instead of full Snowflake for simplicity.

7. Analytics Pipeline

Analytics must never slow down the redirect. The pattern is fire-and-forget asynchronous logging.

Redirect Server
    │
    ├──▶  302 Redirect (synchronous, <5ms)
    │
    └──▶  Kafka Producer (async, non-blocking)
              topic: click-events
              key: short_code (partitioned for ordering)
              value: { short_code, timestamp, ip, user_agent, referrer }
                   │
                   ▼
         Flink / Spark Streaming
         (enriches IP → country, aggregates counts)
                   │
          ┌────────┴────────┐
          ▼                 ▼
    ClickHouse           Redis Counters
  (analytics queries)   (real-time counts)

8. Scaling Considerations

At 1 billion redirects per day (~11,500 rps), a single server is a bottleneck. Here is how to scale each layer:

LayerBottleneckSolutionScale Target
Redirect ServiceCPU / networkHorizontal scale behind LB50+ stateless instances
Redis CacheMemoryRedis Cluster, 6 shards600GB total cache
Database readsIOPSRead replicas (5-10×)99% cache hit rate → low DB load
Database writesWrite throughputMaster with async replication~1,150 writes/sec (10:1 ratio)
CDNGlobal latency301 redirect cached at edge~80% of traffic handled at CDN
AnalyticsWrite throughputKafka partitioned by short_codeMillions of events/sec

9. URL Expiry

URLs with an expires_at timestamp must return a 410 Gone response after expiry. Implement this at two levels:

  • At redirect time: Check expires_at in the cached URL object. If expired, return 410 and delete from cache.
  • Background cleanup job: Nightly cron deletes expired rows from the DB (DELETE FROM urls WHERE expires_at < NOW() AND expires_at IS NOT NULL LIMIT 10000). Use batched deletes to avoid locking the table.

Watch Out — Cache Serving Expired URLs

If a URL is cached in Redis but has expired in the DB, the redirect service might still serve it. Fix by storing expires_at in the Redis value and checking it on each redirect. Set the Redis TTL to expires_at - now() so Redis automatically evicts it at the right time.

10. Trade-offs and Design Decisions

DecisionChoseAlternativeReason
Redirect type302 (analytics mode)301 (performance mode)Analytics requirement overrides cache benefit
ID generationSnowflake + Base62Hash-basedZero collisions, sortable, compact codes
Primary DBMySQL (relational)Cassandra (NoSQL)Strong consistency for write path; read replicas handle scale
Analytics storageClickHouse (columnar)MySQLColumnar storage 100× faster for aggregation queries
Cache strategyCache-asideWrite-throughWe only cache URLs actually accessed (Zipf distribution)

How We Research and Update This Guide

We test the underlying formula or workflow, compare outputs with reliable references, and revise examples whenever the page content changes.

  • The workflow or formula is tested directly in the tool and compared against independent reference examples.
  • Examples are kept practical so readers can verify the result without hidden assumptions.
  • Pages are revised whenever the interface, calculation flow, or surrounding guidance materially changes.

Frequently Asked Questions — URL Shortener System Design