System design interview: design a URL shortener

A URL shortener maps a long address to a short code and redirects anyone who opens the code. The interview is about three decisions: how you generate codes that never collide, how you serve a read-heavy redirect path in a few milliseconds, and what you give up by choosing a 301 or a 302 redirect.

By Published 7 min read

1. Clarify the requirements

Spend the first few minutes agreeing on scope. Interviewers often leave custom aliases, expiry and analytics ambiguous on purpose to see whether you ask.

  • Functional: create a short link for a long URL; redirect a short link to its target; optional custom alias; optional expiry date; click counts per link.
  • Non-functional: redirects must be fast (aim for under 50 ms at the server) and highly available, because a broken short link breaks someone else's page, email or QR code. Creating links can be slightly slower.
  • Out of scope unless asked: user accounts and billing, link editing after creation, a full analytics dashboard.

2. Estimate the scale

Pick round numbers, state them as assumptions and use them to justify choices later. The figures below are illustrative, not any real company's traffic.

Back-of-envelope estimate for a large public URL shortener (assumed figures)
QuantityAssumptionResult
New links100 million a dayabout 1,160 writes a second on average
Redirects10 reads for every writeabout 11,600 reads a second; plan for 3x at peak, about 35,000
Records over 5 years100 million x 365 x 5about 183 billion mappings
Storageabout 500 bytes a record (code, URL, owner, dates)about 90 TB before replication
Code lengthbase62 (a–z, A–Z, 0–9), 7 characters62^7 ≈ 3.5 trillion codes, enough for decades at this rate
Back-of-envelope estimate for a large public URL shortener (assumed figures). Scroll sideways on a small screen.

Two conclusions fall out of the numbers: the data set is too large for one machine, so it has to be partitioned; and the read rate is easy for a cache, because a small share of links (a viral post, a campaign) takes most of the clicks.

3. Define the API

POST /api/v1/links
  body:    { "url": "https://example.com/a/very/long/path", "alias": "spring-sale", "expiresAt": "2027-01-01" }
  returns: 201 { "code": "spring-sale", "shortUrl": "https://sho.rt/spring-sale" }

GET /{code}
  returns: 302 Location: https://example.com/a/very/long/path
           404 if the code does not exist, 410 if it has expired
Two endpoints carry the whole product. Rate-limit the first one per API key or IP address.

4. Sketch the data model

One table (or one key-value item) holds everything the redirect needs
FieldTypeWhy it is there
codestring, primary keyThe lookup key for every redirect; also the partition key
long_urlstring (up to ~2 KB)The target
created_at / expires_attimestampsExpiry checks on read and a cleanup job
owner_idstring, nullableWho may delete it; rate limits per owner
One table (or one key-value item) holds everything the redirect needs. Scroll sideways on a small screen.

Click counts do not belong in this row. Incrementing a counter on every redirect turns the hottest read path into a write path. Send click events to a queue instead (section 7).

5. Generate codes that never collide

This is the decision the interviewer most wants to hear you reason about. There are three common answers, and each has a cost.

Three ways to make a short code
ApproachHow it worksStrengthCost
Hash and truncateMD5 or SHA-256 of the URL, keep the first 7 base62 charactersSame URL always gets the same code; no coordinationCollisions are certain at scale, so every write needs a check-and-retry; two users shortening the same URL share a link
Global counter + base62An auto-incrementing ID, encoded in base62No collisions, short codes, simpleThe counter is a single point of contention; sequential codes are guessable, so links can be enumerated
Pre-allocated key rangesA key service hands each app server a block of IDs (say 10,000) to encode locallyNo collisions and no per-write coordination; scales outA crashed server wastes the rest of its block (harmless with 3.5 trillion codes); one more service to run
Three ways to make a short code. Scroll sideways on a small screen.

A strong default is the range approach: a small coordination service (or a database sequence stepped by 10,000) hands out ranges, and each app server encodes its own IDs. If guessable codes matter, pass the ID through a reversible shuffle (a keyed permutation of the 62^7 space) before encoding, so neighboring IDs give unrelated codes.

Custom aliases use the same table: insert with a conditional write ("only if this code does not exist") and return 409 Conflict if it is taken. Reserve words like api or admin so an alias can never shadow a route.

6. The architecture

  1. Edge
    • CDN (optional, caches 301s)
    • Load balancer
  2. App tier
    • Stateless app servers
    • Rate limiter
    • ID range allocator
  3. Hot path
    • Redis cache (code → URL, LRU)
  4. Storage
    • Key-value store, partitioned by code
    • Replicas in 2+ zones
  5. Async
    • Click event queue
    • Analytics aggregator
    • Expiry cleanup job
Request path for creating and following a short link. Clients reach a CDN and load balancer, then stateless app servers. Redirects read from a Redis cache first and fall back to the partitioned key-value store. Creates take an ID from the app server's pre-allocated range and write to the store. Every redirect also drops a click event on a queue, which an analytics consumer aggregates into a separate store.
  1. RedirectLook up the code in Redis. On a miss, read the store, write the result back to the cache, then answer 302 with the target. Check expires_at before answering.
  2. CreateValidate the URL (scheme, length, a blocklist of known malicious domains), take the next ID from the server's range, encode it, write the row, return the short URL.
  3. CountPublish a small event (code, timestamp, referrer, country) to the queue after the redirect is sent, so analytics never slows the response.

7. Scale it

  • Cache the hot set. With an 80/20 access pattern, caching the most-clicked 20% of a day's links serves most reads from memory. A few hundred GB of Redis, sharded, is plenty. Use LRU eviction.
  • Partition the store by code. A hash of the code spreads load evenly; range partitioning by code would put all new (sequential) links on one partition. DynamoDB, Cassandra or a sharded relational database all work.
  • Keep app servers stateless so you add them behind the load balancer as traffic grows. The only state they hold is their unused ID range.
  • Replicate across zones for availability. Redirects can read from a replica: a link created a second ago being briefly unavailable on one replica is acceptable.
  • Move analytics off the hot path with the queue. Aggregate into per-minute counts per code in a column store or time-series database.

8. What breaks, and what you do about it

Failure modes worth naming
FailureEffectMitigation
Cache cluster downEvery redirect hits the store; latency and load spikeSize the store for cache-miss traffic at normal load; circuit-break and shed analytics first
A link goes viralOne key gets millions of reads a minute (a hot key)Local in-process cache on app servers for the top keys; CDN caching for that code
ID allocator unavailableNew links cannot be createdServers keep a spare range so creates continue for minutes; redirects are unaffected
Abuse: phishing linksYour domain gets blocklisted by browsers and mail filtersScan targets on create and periodically, rate-limit anonymous creates, add a takedown path
Failure modes worth naming. Scroll sideways on a small screen.

9. Trade-offs to discuss

  • 301 or 302. A 301 (permanent) is cached by browsers, which cuts your read traffic but hides repeat clicks and makes a changed or deleted link stick in caches. A 302 (or 307) is counted every time. Most shorteners that sell analytics use 302.
  • SQL or NoSQL. The access pattern is a single-key lookup with no joins, which suits a key-value store. A relational database is simpler to run and is a fine answer for a smaller scope; say where you would switch.
  • Guessable or random codes. Sequential codes are shorter and cheaper; random-looking codes protect private links from enumeration. You can have both with a keyed shuffle.
  • Deduplicate or not. Returning the same code for the same URL saves space but leaks that someone else shortened it, and breaks per-user analytics. Most designs create a new code per request.

10. How to talk through it in the interview

Fictional example

An opening that sets up the whole answer in under a minute
Candidate
Before I draw anything: do we need custom aliases, expiry and click analytics, or just create and redirect?
Interviewer
Aliases and basic click counts, yes. Expiry is optional.
Candidate
Then I will assume about 100 million new links a day and ten redirects per create. That is roughly 12,000 reads a second, so the redirect path is what I will optimize. I will cover code generation, the read path with a cache, storage partitioning, and then analytics off the hot path.
  • Name the read/write ratio early and keep coming back to it.
  • When you pick a code-generation scheme, name the one you rejected and why.
  • Leave time for failure modes. "What happens when the cache dies?" is the most common follow-up.

Practicing this design with Natively

Run the walk-through as a mock interview: ask a friend to play the interviewer on a call, or read the prompt aloud yourself. Natively transcribes the conversation as it happens and, when a question comes in, can suggest talking points for it. Comparing those with what you actually said is a quick way to spot gaps such as failure modes or the redirect status code. After the session, the transcript and notes stay on your computer, so you can see where you ran long and what you skipped.

Questions people ask

How many characters should a short code have?

Seven base62 characters give about 3.5 trillion combinations, which covers 100 million new links a day for more than 90 years. Six characters (about 57 billion) is enough for a smaller service. Say the arithmetic out loud in the interview; the number itself matters less than showing you checked it.

Should a URL shortener use a hash of the URL?

Hashing gives the same code for the same URL without coordination, but a truncated hash collides at scale, so every write needs a check and a retry. A counter or pre-allocated ID ranges encoded in base62 avoid collisions entirely, which is why most interview answers prefer them.

Is a 301 or a 302 redirect better for a URL shortener?

A 301 is permanent, so browsers cache it and later clicks never reach your servers: less load, but no click counts and no way to change the target. A 302 (or 307) is temporary, so every click is seen. Services that report analytics generally use 302.

Where to go next

Practice with the same assistant you would use on a call: it listens, transcribes and suggests answers from your own resume and the job description.

Facts on this page were last checked on . Product details come from the Natively app itself; if something here is out of date, tell us at hello@heynatively.com.

Keep reading

Put it on your own machine.

A live-conversation assistant that runs locally. Your conversations stay on your machine.

macOS 12+ · Windows 10/11

© 2026 Natively AI Private Limited

Source-available under the Natively Personal Use Source License v1.0 · IP geolocation by DB-IP