Playbook / General system design / Design a URL shortener

Design a URL shortener

Expected question

"Design a URL shortener (bit.ly-style). How do you generate short codes, redirect with low latency, and track analytics at scale?"

Variant forms

Same design, different framing:

  • "Design a URL shortener for 100M new links/month and 10B redirects/month."
  • "How do you generate short codes — hash, counter, or random — and avoid collisions?"
  • "Design custom branded domains per enterprise customer."
  • "Our short links were used for malware — architect abuse detection and takedown."
  • "How do you expire links and support UTM analytics without slowing redirects?"
  • "Design read-heavy redirect path with <10ms P99 globally."
  • "Scale the analytics pipeline separately from the redirect hot path."

Where this actually gets asked

Canonical warm-up / screening system-design question (Bitly-style) across Google, Amazon, Meta, and startups. Often used before a harder follow-up in the same loop. Staff+ depth: ID generation, read-heavy caching, 301 vs 302, analytics, and abuse.

Executive summary

30-second thesis

I'd mint Base62 codes from allocated counter ranges, cache code→URL on the redirect hot path, default 302 + async click logs — and treat create-path abuse as a first-class concern.

2-minute answer

Clarify create QPS vs redirect QPS (often ~100:1 H), custom aliases, and analytics accuracy. Default: range-allocated IDs (or Snowflake → Base62), durable mapping, Redis/CDN in front of redirects. Prefer 302 when analytics matter or destinations can change; 301 only when immutable and you're okay with browser/CDN caching forever. Analytics off the hot path via queue. Custom aliases need a reservation transaction; reconcile if DB write wins and cache fill fails. Rate-limit creates; async malware scan with suspend. Viral codes: edge cache + shard awareness so one hot key doesn't melt a single DB partition.

What I'd refuse: 301-caching a destination that can still change, or an open create path that becomes a malware CDN. Clock: lock mint + redirect hot path; skip a full analytics warehouse.

Quantitative trade-offs

DecisionTrade-off and reversal evidenceEvidence class
302 vs 301302 keeps counts/flexible targets; reverse to 301 when immutable and analytics are approximate OK.H
Counter/Base62 vs hash-and-retryRanges avoid collision storms; reverse to hash only for tiny scale.H
Cache-aside redirect vs always-hit DBCache for p99 <10–50ms (H); reverse only if consistency of destination changes forces shorter TTL + purge.H

Migration and rollout

The hot path is redirect. Analytics and custom aliases are second waves.

  1. Mint + durable map first; 302 default. Do not 301 a destination that can still change.
  2. Cache code→URL with purge on update/takedown.
  3. Async click logs after the 302 is correct. Don't block redirect on the warehouse.
  4. Abuse: rate-limit creates + malware scan with suspend before custom branded domains.
  5. Rollback = previous map. Kill switch = 410/suspend a code; open-create is how you become a malware CDN.

Org ownership and operating model

  • Links platform owns mint, redirect, and 301/302 policy.
  • Abuse / trust owns create rate limits and takedown.
  • Analytics owns the async pipeline — not the p99.
  • Enterprise owns branded domains and reservation transactions.
  • SRE owns hot-code cache and shard awareness.

What I'd ask them

  1. Can destinations change after create?
  2. Custom aliases and enterprise branded domains in v1?
  3. Redirect SLA and analytics lag tolerance?

Requirements

Functional

  • Create a short code for a long URL; optional custom alias and TTL.
  • Redirect short URL to long URL quickly.
  • Basic click analytics (count, optional referrer/country aggregates).

Non-functional

  • Extremely read-heavy (≈100:1 (H)); redirects P99 < 10–50ms with cache.
  • Unique short codes; no collisions serving wrong destinations.
  • High availability for redirects even if write path is degraded.
  • Abuse: malware URLs, spam creation rate limits.

Core entities

  • Link: short_code, long_url, owner_id, created_at, expires_at, status.
  • Click event: short_code, ts, metadata (coarse).
  • Alias reservation: custom code lock during create.

API / interface

POST /v1/links
Authorization: Bearer <token>
{ "url":"https://example.com/very/long", "custom_code?":"launch", "ttl_days?":90 }
→ 201 { "code":"aB3xY9", "short_url":"https://short.ly/aB3xY9" }
→ 409 alias_taken | 400 invalid_url

GET /{code}
→ 302 Location: <long_url>  (or 301 if immutable / analytics-light)
→ 404 | 410 gone

GET /v1/links/{code}/stats
→ { "clicks":123456, "by_day":[...] }

Staff+ callout: say whether redirects are 301 (cacheable, weaker analytics) or 302 (accurate counts).

Data Flow

Create allocates code → persist mapping → cache. Redirect reads cache then DB; async click log.

Rendering architecture diagram…

High-level design

Rendering architecture diagram…

Deep dive 1: ID generation

Base62 encode a counter (Snowflake-style or pre-generated ranges per instance) beats hash-and-retry for uniqueness at scale. Custom aliases need a reservation transaction. See also 05.

Deep dive 2: cache and 301/302

Cache long URLs by code with TTL; negative-cache 404 briefly. 301 lets browsers/CDNs cache forever — cheap but bad for accurate analytics and destination changes. 302 + async analytics is the usual product choice for Bitly-like services.

Deep dive 3: abuse

Rate-limit creates per owner/IP; scan destinations against malware lists asynchronously (mark suspended). Cap custom alias namespace squatting.

Shard by short_code; a viral link can melt one shard — edge/CDN cache + read replicas + redirect rate limits. Custom aliases need transactional uniqueness; if DB write succeeds and cache fill fails, a reconcile job must repair (avoid redirect 404). In 45 minutes, IDs + cache + 301/302 is the core; analytics is async.

What's expected at each level

  • Mid-level: hash URL, store map, redirect.
  • Senior: Base62 IDs, cache, basic stats.
  • Staff+: collision-free ID ranges, 301/302 trade-off, write/read HA split, abuse.
  • Principal: global replication and analytics cost at extreme redirect volume. Who inherits: the edge + data. Fail-closed: don't redirect malware/phishing once flagged. Clock: lock ID space + 301 path; skip a full ads/analytics platform.

Follow-up questions to expect

  • "What do you skip in 45 minutes?" A BI platform. Cover code mint, redirect, abuse, async analytics.

  • "How long should codes be?" — 62^7 is ~3.5T (H); pick length from years of creates × safety margin, not vibes.

  • "Can destinations change?" — If yes, don't 301 (or purge CDN hard). Mutable targets want 302.