Diagrammatic

URL Shortener (like bit.ly) — System Design Interview Practice

Design a URL shortening service that can handle millions of URLs and redirect requests efficiently. Work through the requirements, architecture trade-offs, and an interactive design review.

Concepts and architecture decisions to consider

  • web servicesConcept to explore
  • scalabilityConcept to explore
  • cachingConcept to explore
  • databasesConcept to explore
  • load balancingConcept to explore

Interview prompt

Design a URL shortening service like bit.ly that creates compact links for valid destination URLs and redirects them with low, predictable latency at high read volume.

  • Separate the link-creation path from the read-heavy redirect path and give each path an explicit latency and availability budget.
  • Choose a short-code strategy, explain its capacity and collision behavior, and make creation retries idempotent.
  • Keep redirect resolution cache-first and move click analytics, aggregation, and other non-critical work off the redirect path.
  • Cover aliases, expiration, deletion, privacy, destination safety, hot links, and degraded behavior.

Requirements and scale assumptions

  • Create a compact short URL for a valid destination URL.
  • Redirect a short code to its current destination while enforcing status and expiration.
  • Support optional custom aliases and expiration dates.
  • Provide aggregate click analytics by time, referrer, device, and coarse location.
  • Let an authenticated owner update, disable, or delete a link.
  • Make create requests safe to retry with an idempotency key.
  • Keep redirect p99 latency below 100 ms for the normal cache-first path.
  • Target 99.99% availability for redirects and 99.9% availability for link creation.
  • Make newly created links readable after the create response and keep updates ordered per link.
  • Make private or unlisted codes difficult to enumerate and rate-limit abuse without slowing healthy redirects.
  • Treat analytics as eventually consistent and never block a redirect on analytics availability.
  • Create 100 million new links per month and serve 10 billion redirects per month, with sharp viral peaks.
  • The redirect-to-create ratio is approximately 100:1; a hot code can receive a disproportionate share of traffic.
  • Retain links for five years by default, or until a configured expiration date; raw click events use a separate retention policy.
  • Use a seven- or eight-character Base62 code space and partition durable lookups by a hash of the short code.
  • Create traffic: ~40 req/s average — 100 million link creations per month before burst and regional capacity headroom.
  • Redirect traffic: ~3.9K req/s average — 10 billion redirects per month; viral links and campaigns determine peak sizing.
  • Availability: 99.99% redirects / 99.9% creates — The read-heavy redirect path has the stricter availability objective.
  • Redirect latency: p99 < 100 ms — The cache-first redirect budget excludes slow analytics and dashboard work.
  • Code capacity: 62^7 = 3.5T — Seven Base62 characters provide a large namespace; eight characters leave additional growth room.

Key entities

  • ShortURLshortCode (PK), originalUrl, userId (nullable), createdAt, expiresAt (nullable), status, version

    Authoritative link metadata and lifecycle state. The primary read is shortCode to originalUrl; owner and status indexes support management and revocation.

  • ClickEventeventId, shortCode, timestamp, country, device, referrer

    Privacy-scoped redirect telemetry emitted asynchronously after the redirect decision; it is not part of redirect correctness.

  • AnalyticsAggregateshortCode, bucket, clicks, uniqueEstimate, updatedAt

    Rebuildable time-bucketed click totals served to dashboards with an explicit processing watermark.

  • IdempotencyRecorduserId, idempotencyKey, requestHash, shortCode, createdAt, expiresAt

    Bounded record that maps a retried create request to its original result and rejects reuse with a different payload.

Data flow

  1. 1. Create and commit a short linkThe gateway validates the destination, applies abuse and rate-limit policy, allocates a unique code, and commits ShortURL metadata with an idempotency record before returning success.
  2. 2. Prime the read pathAfter the durable write, the service writes or invalidates the redirect cache and returns the code only after the create result is readable from the authoritative region.
  3. 3. Resolve a redirectFor GET /{shortCode}, the edge or redirect cache checks status and expiry on a hit; a miss reads the link store, fills the cache, and returns a 302 by default or a 301 when the link is intentionally immutable.
  4. 4. Emit click telemetry asynchronouslyThe redirect decision returns immediately while a buffered event is sent to a durable queue; analytics workers deduplicate events and build time-bucketed aggregates.
  5. 5. Expire, update, or delete safelyOwner commands and lifecycle workers change the authoritative status, invalidate edge and regional caches, and retain only policy-approved link, click, and audit data.

Deep dives and trade-offs

  • Short-code generation and collision handlingA sequence, Snowflake-style ID, or another unique numeric allocator can be Base62 encoded into seven or eight characters; a random opaque ID reduces enumeration but needs collision retries. Enforce uniqueness with a database constraint or conditional write. Custom aliases should return a conflict when already reserved, and the same idempotency key should return the original create result. Do not hash only the destination URL if two owners need separate links, expiry, or analytics; the identity of the link is a product decision, not just an encoding trick.
  • 301 versus 302 redirectsUse 302 or 307 when the destination may change, because clients and intermediaries should not cache the mapping as permanently; use 301 only for intentionally immutable links. Redirect status and Cache-Control headers affect how quickly updates, deletion, and revocation take effect, so cache policy belongs in the lifecycle design. Analytics should describe the redirect decision and accepted event, not depend on the browser making a second request or on a synchronous analytics write.
  • Database choice and access patternsA key-value or wide-column store fits the dominant shortCode-to-URL lookup and horizontal redirect scale; a relational store is attractive when owner queries, transactions, and alias constraints dominate. Keep the authoritative ShortURL row small and index owner/status/expiry operations separately from the hot redirect key. Whichever store is chosen, require conditional writes for alias allocation, optimistic version checks for updates, and multi-zone replication for the redirect SLO.
  • Caching and cache stampedesUse edge caching plus a regional Redis-style cache-aside layer for active mappings, with TTLs that balance hit rate against revocation speed. Coalesce concurrent misses, add jitter to expirations, and replicate or isolate exceptionally hot codes so one viral link does not overload one cache shard or origin row. Negative-cache unknown or disabled codes for a short period, but keep the TTL bounded so a newly created or re-enabled link is not hidden for long.
  • Partitioning and hot linksPartition durable lookups by a hash of shortCode rather than raw sequential IDs, which distributes writes and avoids sequential-key hotspots. A single viral code can still be hot after hashing; absorb it with edge and regional cache replicas, request coalescing, and per-key rate protection. Keep code allocation and redirect lookup independently scalable because creation volume is much smaller than redirect volume.
  • Availability and degraded redirectsThe redirect path should require only edge/cache and a replicated link store; analytics, dashboards, abuse re-scoring, and cleanup workers must not be synchronous dependencies. On a regional cache failure, fail over to another cache or the authoritative store with bounded retries and circuit breakers rather than retrying a slow dependency indefinitely. Do not serve an unbounded stale destination after explicit deletion or disablement; use versioned invalidation and a bounded stale policy.
  • Read-after-write and eventual consistencyAfter creating a link, return success only when the authoritative write is committed and the serving region can resolve the new code, or route the first read to that authority. Use a per-link version or conditional update so cache refreshes cannot overwrite a newer destination with an older one. Click totals and dashboard dimensions can be eventually consistent as long as the UI exposes a freshness watermark and the raw event stream remains replayable.
  • Expiration, updates, and deletionTreat expiration as a serving decision based on expiresAt, with background TTL cleanup as storage maintenance rather than the only enforcement mechanism. Updates and deletes write the authoritative status first, then invalidate edge and regional caches; tombstones prevent stale replicas from resurrecting a code. Retain audit and click data only for the configured policy, and make cache invalidation observable so operators can measure revocation delay.
  • Abuse, security, and enumerationAllow only supported URL schemes, normalize destinations, scan or score suspicious domains, and provide reporting and takedown controls for phishing or malware. Rate-limit creation, custom-alias attempts, and redirect abuse separately; do not let a bad destination or bot campaign exhaust the redirect budget for healthy links. Use opaque codes where privacy matters, avoid exposing sequential volume through the public namespace, and minimize or hash analytics identifiers before retention.
  • Asynchronous click analyticsEmit a compact ClickEvent after the redirect decision through a durable buffer or queue; the user-visible 302 must not wait for a broker, worker, or analytics database. Use event IDs and idempotent aggregation so at-least-once delivery does not inflate counts; late events should update a bounded correction window. Separate raw-event retention from dashboard aggregates and publish processing lag so owners understand when analytics is delayed.
  • Sequential IDs versus random codesUse a unique ID plus Base62 for compact capacity, then add an opaque permutation or random allocation when enumeration resistance is a stronger requirement. Purely sequential public codes reveal volume and can create hot allocation patterns; purely random codes require collision checks and retries.
  • 301 versus 302 by link lifecycleUse 302 or 307 for mutable links and 301 for links explicitly declared immutable, with cache headers that match revocation expectations. Permanent caching improves repeat latency but makes destination updates and deletion harder to propagate quickly.
  • Key-value store versus relational databaseChoose a key-value store when shortCode lookup and scale dominate; choose relational storage when owner workflows, transactions, and reporting relationships justify it. A key-value design needs deliberate secondary indexes and management queries, while a relational design needs careful partitioning and read replicas at redirect scale.
  • Cache TTL versus immediate revocationUse cache-aside TTLs with targeted invalidation and a versioned status check for updates, deletes, and abuse takedowns. Long TTLs improve hit rate but extend stale-destination risk; short TTLs reduce that risk at the cost of origin load.
  • Synchronous versus asynchronous click loggingBuffer click events and process them asynchronously so redirect availability and latency remain independent of analytics health. Async processing can delay or correct totals, so the product must expose freshness and accept at-least-once aggregation semantics.
Diagrammatic — system design practice and architecture review.