Skip to main content
System DesignInterview Prep

Design a URL Shortener: System Design Interview Walkthrough

A structured answer to the URL shortener (TinyURL) question: requirements, capacity estimates, key generation strategies, database choice, caching, redirects and the follow-ups that separate strong candidates.

InterviewPilot TeamOctober 17, 20264 min read

URL shortening looks trivial and tests whether you can reason about scale, ID generation and read-heavy design. Pair this with the system design cheat sheet.

1. Requirements

Functional: create a short URL, redirect to the long URL, optional custom alias, optional expiry, optional click analytics. Non-functional: very low redirect latency, high availability, short codes unguessable enough, scalable.

2. Estimates

Assume 100M new URLs per month and 100:1 read:write.

  • Writes: about 40 per second
  • Reads: about 4,000 per second on average, with higher peaks
  • Storage: 100M × 500 bytes ≈ 50 GB/month

The numbers tell you: a cache plus a partitioned store is plenty.

3. API

POST /api/v1/urls   { longUrl, alias?, expiresAt? }  -> { shortUrl }
GET  /{code}         -> 301/302 redirect to longUrl

4. Generating the short code

ApproachIdeaTrade-off
Counter + base62Unique ID from a counter, encoded in base62No collisions; needs a distributed counter (ranges handed to app servers)
Hash + truncateMD5/SHA of the URL, take 7 charsStateless, but collisions need a retry or check
Key generation servicePre-generate random keys, hand them outFast writes; extra component
Random + checkRandom code, insert if absentSimple; retry loop at scale

A common answer: counter ranges per server, base62-encode them, and add a random salt if guessability matters.

5. Data model and storage

code (PK) -> longUrl, userId, createdAt, expiresAt. The access pattern is single-key lookup, so partition by code. Add a TTL for expiry.

6. Reads: cache first

  • Cache code -> longUrl in Redis with an LRU policy; the hot set is small.
  • On miss, read the database and populate the cache.
  • Handle negative caching for unknown codes to avoid hammering the DB.

7. Scaling and reliability

  • Stateless app servers behind a load balancer
  • Database sharding by code, with replicas for read availability
  • Rate limit creation to prevent abuse (see rate limiter design)
  • Detect malicious URLs, and validate input

8. Follow-ups

  • How do you support custom aliases without collisions?
  • How do you count clicks without slowing redirects? (async events to a queue)
  • How do you expire and reclaim codes?
  • What if the counter service fails? (pre-allocated ranges)

Worked example: base62 encoding

To turn a numeric ID into a short code, repeatedly divide by 62 and map remainders to the alphabet of digits, lower-case and upper-case letters. Seven characters give 62 to the power of 7, about 3.5 trillion codes. If a counter hands out 100 million IDs a month, seven characters last for thousands of years.

If you worry about guessability (sequential codes let someone enumerate all URLs), shuffle the ID with a reversible permutation or add a random component before encoding.

Distributed ID generation

OptionHowTrade-off
Single counterOne database sequenceSimple but a bottleneck
Range allocationEach app server leases a block of 1,000 IDsFew round trips; gaps after crashes
Snowflake-style IDsTimestamp, machine ID, sequenceNo coordination; longer codes
Key generation servicePre-computed unique keys in a storeFast; extra service to run

Range allocation is a good default answer.

Handling custom aliases

Check availability with an insert-if-absent operation (a conditional write) rather than read-then-write, to avoid races. Reserve a blocklist of words such as admin or api routes.

Analytics without slowing redirects

Redirects should return immediately. Emit a click event (code, time, country, user agent) to a queue and process it asynchronously into aggregated counters. This is also where a 302 is needed, since a cached 301 would skip your server.

Security and abuse

  • Validate and normalize submitted URLs; reject unsupported schemes
  • Scan against known-malicious URL lists
  • Rate limit creation per user and IP (rate limiter design)
  • Prevent open-redirect abuse by showing an interstitial for flagged links
  • Allow owners to disable a code

Expiry and cleanup

Store an expiry time and either rely on a database TTL or run a background job that deletes expired rows and frees the cache. Decide whether expired codes may be reused; usually they are not, to prevent old links from pointing somewhere new.

Interview tips

Spend your first five minutes on requirements and estimates, because the read-heavy ratio drives every later choice. Finish by naming the single points of failure (ID generator, database) and how you remove them.

Frequently asked questions

Was this article useful?

Discussion

No comments yet. Been through an interview like this? Your experience could help the next candidate.

You’re posting anonymously — it appears after a quick review. Sign in to post under your name instantly.
0/2000

Bring backup into your next round.

Install InterviewPilot, upload your resume, and walk in with structured answers a keystroke away — visible to you and nobody else.