Design a URL shortener
Picture
Creating a link stores code → destination. Opening it reads that mapping and returns a redirect. Reads may greatly outnumber creates.
Predict
Does base-62 encoding turn a sequential ID into an unguessable secret?
Walk through the small example
No. It changes representation, not unpredictability or authorization.
Mental model and runnable Python model
Begin with a durable mapping store and an indexed unique code. Create validates supported destination syntax, reserves a code atomically, and stores owner/status/expiry. Redirect reads an active mapping, optionally through cache, and returns the chosen redirect status. Keep analytics asynchronous so tracking failures do not block navigation unless requirements say otherwise. Scale the read path first, then partition by code if storage or throughput requires it. Do not fetch arbitrary destinations on the redirect server merely to validate them; any preview-fetch feature adds SSRF and content-safety concerns.
Scope and API
POST /links creates a mapping with owner, destination, optional alias, and expiry. GET /{code} redirects only active mappings. Decide whether destinations may change and whether creation supports idempotency. Read-heavy traffic is an assumption to measure, not a universal ratio.
Data and write path
Store code as a unique key with destination, owner, created_at, expires_at, and status. Allocate IDs through a durable coordinated mechanism or use random candidates with a uniqueness constraint and collision retry. A restarted local counter is not a production allocator.
Read path and scaling
Resolve code through cache, then the mapping store. Keep analytics events off the redirect critical path where possible. Use versioned cache entries or invalidation for mutable destinations and blocks. Partition by code only when measured scale requires it.
Failures and tradeoffs
Define unknown, expired, and blocked responses; rate-limit creation and detect abuse. Permanent redirects can be cached by clients, making later changes harder. A cache outage must not overwhelm storage; use bounded fallback, load shedding, and observability.
What the function promises: Shortener is a single-process in-memory mapping model with sequential base-62 codes and http/https syntax checks. It does not run an HTTP server, persist IDs, expire links, or provide abuse detection.
from urllib.parse import urlsplit
def base62(number):
alphabet = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
if number < 0:
raise ValueError("ID must be nonnegative")
result = ""
while number:
number, digit = divmod(number, 62)
result = alphabet[digit] + result
return result or "0"
class Shortener:
def __init__(self):
self.next_id, self.links = 1, {}
def create(self, destination):
parsed = urlsplit(destination)
if parsed.scheme not in {"http", "https"} or not parsed.hostname or parsed.username:
raise ValueError("Unsupported destination")
code = base62(self.next_id)
self.next_id += 1
self.links[code] = destination
return code
def resolve(self, code):
return self.links[code]
service = Shortener()
a = service.create("https://example.com/a")
b = service.create("https://example.com/b")
assert a != b and service.resolve(a) == "https://example.com/a"
assert base62(62) == "10"
Work and storage: O(log ID) encoding and expected O(1) local lookup. Distributed uniqueness, durability, cache hit rate, and database latency dominate a real service.
When to recognize it: Redirects can look up popular codes nearby. Updates, expiry, or abuse blocking need an invalidation and freshness policy.
Check before applying it: Short IDs are not secrets, and cached redirects need explicit mutation and abuse policies.
Change one rule
A link must be blocked immediately for abuse. Can a day-long cached redirect remain authoritative?
See what changes
No. Define a fast revocation path, bounded stale behavior, or a checked blocklist. Immutable-cache assumptions conflict with immediate mutable access policy.
Connect
Choose storage from reads and writes: A unique durable mapping is the core storage invariant.
Make an API safe to call again: Specify create retries and redirect semantics at the API boundary.