SDSystem design
Start learning free

LESSON 39 OF 140 · DESIGN: READ-HEAVY & COUNTING

Design a URL Shortener

Caching, unique codes, 301 vs 302 Read the written walkthrough

SYSTEM DESIGN · INTERVIEW WALKTHROUGH

Design a URL Shortener: System Design Interview, Step by Step

What the interviewer is asking

"Design a URL shortener" (a service like bit.ly or TinyURL) is one of the most common system design questions, and often the first one people meet. The system is small enough to finish in 45 minutes, yet it touches the ideas every design interview checks: scoping, estimates, an API, a data model, caching, unique IDs and trade-offs.

The interviewer is not looking for one right diagram. They watch how you get there: what you ask first, which numbers you work out, and why you pick one option over another. The walkthrough below follows the order that works in any design interview.

1. Scope: what version 1 must do

Start by agreeing on the smallest version that is still useful. A URL shortener needs two things:

  • Make a short link from a long one, such as sho.rt/x7Kp2.
  • Open a short link and land on the long page.

Everything else is an extra you name and park for later: click statistics, custom codes (sho.rt/my-shop), accounts with a list of your links, and links that expire. Saying this out loud keeps the design small and shows the interviewer you can prioritize.

2. Data model: what we store

Each link is one record: the short code and the long URL, plus when it was created. That is a key and a value, and the busy path only ever looks a link up by its code.

So almost any store works: a key-value store, or one SQL table with the code as its primary key. Leave out what version 1 doesn't use: click counts belong to the analytics feature, and the long page itself is never copied.

3. The API: two calls

Two features, two calls:

POST /links        {"url": "https://shop.com/sale/summer?id=123"}
  → 201 Created    {"code": "x7Kp2"}

GET /x7Kp2
  → 302 Found      Location: https://shop.com/sale/summer?id=123

The redirect matters: the server never sends the long page itself, only a few bytes telling the browser where to go. Mention the errors too: 400 for a URL that is not valid, 404 for a code that does not exist, and 429 when a client creates links too fast.

4. The numbers: how busy is it?

Ask for, or propose, the traffic. A typical set: 100 million new links a month, each opened about 100 times.

  • Redirects: 100 million × 100 = 10 billion a month. A month has about 2.6 million seconds, so that is about 4,000 redirects a second on average.
  • New links: 100 million ÷ 2.6 million seconds ≈ 40 a second. Reads outnumber writes 100 to 1.
  • Busy hours: traffic is not flat; at about 5× the average, plan for roughly 20,000 redirects a second.
  • Storage: 100 million links a month for 10 years is 12 billion links. At about 500 bytes each, that is around 6 TB.
  • Code length: with 62 characters (0–9, a–z, A–Z), 6 characters give 62⁶ ≈ 57 billion codes, enough for 12 billion links; 7 characters (about 3.5 trillion) leave plenty of room.

The one number that drives the design is the ratio: opening links is the busy path, so that is the path to make fast.

5. High-level design: two services

With 40 creates and 4,000 redirects a second, putting both in one app means a rush of clicks can slow down making links, and you cannot add servers for just one side. Split them:

  • Create service: checks that the long URL is valid, makes a new code, saves code → long URL.
  • Redirect service: looks up the long URL for a code and answers with the redirect.

Both share the links database. Now you can run a few Create servers and many Redirect servers, each side sized for its own traffic.

6. Scaling the busy path: a cache

At 20,000 redirects a second, a single database server that answers about 5,000 lookups a second is asked four times what it can do. Adding Create servers would not help (creates are only 40 a second), and longer codes would not change the number of lookups.

The fix is a cache in front of the database: the Redirect service checks the cache first and reads the database only on a miss (cache-aside). It works especially well here because a link never changes once it is made, and a small share of links gets most of the clicks.

If the cache answers 90% of lookups, only the misses reach the database: 10% of 20,000 is 2,000 a second, well under its limit. Read replicas of the database are a further step if the misses grow.

7. Making unique codes

This is the deep dive most interviewers ask for. Three common options:

  • Random codes: pick 7 random characters, check the code is free, save it. Simple, but every create needs an extra lookup, and the check and the save must be one step (or the database must reject a duplicate), or two servers can both claim the same code.
  • A hash of the long URL: the same URL always gets the same code, but a hash cut down to 7 characters collides, so you still need a check.
  • Numbers from a counter: give each link a number (1, 2, 3…) and write it in base 62: link number 1,000 gets g8, link number 1,000,000 gets 4c92. Numbers never repeat, so codes never clash.

The counter has a trap. If each Create server reads "the last number used" and adds 1, two servers working at the same moment read the same number and make the same code; in a key-value store the second save silently replaces the first, and a shared link now opens someone else's page.

The usual fix: a shared counter hands each Create server its own block of numbers, say 1 to 1,000,000 for one server and the next million for another. Blocks never overlap, and each server asks the counter only once per million links. One thing to mention: counter codes come out in order, so they are easy to guess; if links must stay private, shuffle the numbers before encoding them.

8. 301 or 302?

Both redirect, but they behave differently. A 301 means "moved for good": the browser remembers it and next time goes straight to the long page without asking you. A 302 means "for now": the browser asks every time.

Pick 302 when you plan to count clicks or let people change or remove a link later: every open passes through your servers. The cost is more traffic, which the cache already absorbs. A 301 saves that traffic but gives up the counting and the control.

9. What breaks first

  • A link that goes viral: the cache absorbs it; make sure one hot key cannot overload one cache server.
  • The counter: it is shared by every Create server, so run it with a replica; blocks mean a short outage does not stop link creation.
  • The database growing to terabytes: split it across servers by code (sharding).
  • Click statistics (version 2): record each open on a queue and count in the background, so the redirect stays fast.
  • Abuse: limit how fast one client can create links (rate limiting).

Summary: the order to follow

  1. Scope: make a link, open a link; park the extras.
  2. Data: code → long URL.
  3. API: POST /links, and GET /{code} answered with a redirect.
  4. Numbers: about 40 creates and 4,000 redirects a second, 20,000 at busy hours.
  5. Services: split the create and redirect paths.
  6. Scale: a cache in front of the database.
  7. Codes: blocks of numbers from a shared counter, written in base 62.
  8. Redirects: 302, so clicks can be counted later.

The same steps work for any "design X" question: scope, data, API, the busy path and its numbers, services, scale, then the deep dives the interviewer cares about.

Keep going with system designInteractive lessons from the building blocks to Twitter, Uber and payments, the way interviews ask. Free to start.
Start learning free