Design a URL shortener

System design Mid-level Python 3.13+ PostgreSQL 17+ Reviewed By Best Latest Apps

Predict the output

What does the line print?

Python 3.13
import string

ALPHABET = string.digits + string.ascii_letters  # 62 symbols


def base62(n: int) -> str:
    if n == 0:
        return ALPHABET[0]
    out = []
    while n:
        n, r = divmod(n, 62)
        out.append(ALPHABET[r])
    return "".join(reversed(out))


print(base62(125), 62**7)

Options
Hint 1 of 1

125 ÷ 62 = 2 remainder 1

Choose an answer, then open Show answer below to check it.

Boxes for client, API, ID allocator, Postgres, Redis cache and click queue; trace a create and a redirect.
Text version

Boxes on the board, in order:

  1. Client
  2. API
  3. ID allocator
  4. Postgres
  5. Redis cache
  6. Click queue
Show answer

Answer: A 21 3521614606208

Correct. 125 = 2×62 + 1, and 62⁷ is about 3.5 trillion.

Base 62 repeatedly divides by 62 and keeps the remainder: 125 is 2 × 62 + 1, so the digits are '2' and '1'. Seven characters give 62⁷, about 3.5 trillion codes.

Why not the others?
  • B 125 3521614606208. 125 is encoded in base 62.
  • C 12 3521614606208. Remainders are emitted least-significant first, then reversed.
  • D 21 62000000. 62⁷ is far larger.

30-second answer

Writes create a short code and store code → URL; reads look up the code and return a 301 or 302 redirect. Generate codes by base62-encoding a unique id (from a sequence or a range allocator per server), or hash plus collision check. Store in a key-value-friendly table with the code as primary key. Reads dominate, so cache hot codes in Redis and at the CDN. Add expiry, abuse checks on target URLs, and click counts sent asynchronously through a queue.

Practise saying it · 40 s. No recording, just a timer

Design a URL shortener.

Say your answer out loud, then check which of these points you covered:

  • Estimates: read-heavy
  • Base62 of unique id
  • Code as primary key
  • Cache + CDN
  • Async click counts

Deep dive

Seven base62 characters give about 3.5 trillion codes.

301 is cached by browsers (fewer hits, less analytics); 302 keeps every click visible.

Range allocation: each server reserves blocks of 1,000 ids so it doesn't hit the sequence per write.

Custom aliases need a uniqueness check.

Base62 codes and the size of the space Ran on Python 3.13.5 · mypy --strict
Python 3.13
import string

ALPHABET = string.digits + string.ascii_letters  # 62 symbols


def base62(n: int) -> str:
    if n == 0:
        return ALPHABET[0]
    out = []
    while n:
        n, r = divmod(n, 62)
        out.append(ALPHABET[r])
    return "".join(reversed(out))


print(base62(125), 62**7)

Common mistakes

  • Counting clicks synchronously on the redirect path.
  • Random codes without collision handling.
  • No abuse or malware checks.

Senior take & follow-ups

Requirements and estimates, code generation, storage, read path with caching, async analytics, abuse.

Follow-up questions

Think of your answer first, then open the hint.

  1. 301 or 302?

    Show hint

    301 saves load; 302 keeps analytics accurate.

  2. How do you avoid a single id sequence bottleneck?

    Show hint

    Allocate id ranges per server.

Suggest a fix if something here is wrong or out of date.