Design a URL Shortener System
Design a production-grade URL shortener using the RESHADED framework.
Introduction
30 Second Summary
Every time you click a shortened link, a system maps that tiny URL to the full address in milliseconds. Designing that system from scratch is one of the most popular interview questions at top tech companies.
In this project, you will produce a complete system design document and architecture diagram for a URL shortener service, following the structured framework that FAANG interviewers use to evaluate candidates.
What You'll Build
A production-grade system design document and Excalidraw architecture diagram for a URL shortener service (like bit.ly), covering requirements, capacity estimation, storage schema, API design, and trade-off analysis.
By the end of this project, you'll have:
- A complete system design document following the RESHADED framework used in FAANG interviews.
- A professional architecture diagram in Excalidraw showing all major components and data flow.
- Back-of-envelope capacity estimation calculations and trade-off analysis that demonstrate the reasoning interviewers score highest on.
- Secret Mission: Extend your design to handle analytics tracking and discuss how it changes your architecture, database, and cost profile.
Are there any prerequisites?
This project assumes basic backend development experience. You should be familiar with concepts like APIs, databases, and servers. No specific programming language or cloud platform is required.
Set Up Your Design Workspace
A system design interview expects you to present two things: a structured document walking through your decisions, and a visual diagram showing how the components fit together.
In this step, you'll open Excalidraw for your architecture diagram and create a Markdown design document structured with the RESHADED framework.
In this step, get ready to:
- Open Excalidraw in your browser for architecture diagramming.
- Create a Markdown design document with RESHADED framework section headings.
- Save both files locally to your machine.
Open Excalidraw in your browser
Excalidraw is a free, browser-based whiteboard tool that produces hand-drawn-style diagrams. No account or installation is needed.
- Open your browser and navigate to https://excalidraw.com.
- You'll see a blank canvas with drawing tools along the top and left side.
What is Excalidraw?
Excalidraw is an open-source diagramming tool that runs entirely in your browser. It produces the informal, hand-drawn-style boxes and arrows that are common in system design presentations.
Files save locally as .excalidraw JSON files. You can also export to PNG or SVG later for sharing.
Create your design document
The RESHADED framework gives you a repeatable structure for system design interviews. Each section maps to what interviewers evaluate you on.
- Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
- Type the name of your text editor and press Enter to open it.
- Create a new file and save it as url-shortener-design.md on your Desktop.
- Paste in the following RESHADED framework headings:
# URL Shortener System Design
## 1. Requirements
## 2. Estimation (Back-of-Envelope)
## 3. Storage
## 4. High-Level Design
## 5. APIs
## 6. Deep Dive: Key Generation Strategy
## 7. Evaluation
What do these sections cover?
- Requirements captures what the system must do (functional) and how well it must do it (non-functional).
- Estimation uses back-of-envelope math to size the system (queries per second, storage, bandwidth).
- Storage defines your database schema and justifies your database choice.
- High-Level Design names every major component and how data flows between them.
- APIs specifies the exact endpoints clients call.
- Deep Dive goes deep on the hardest technical problem in the system.
- Evaluation summarizes trade-offs, failure modes, and cost reasoning.
- Save url-shortener-design.md.
- Confirm you see all seven numbered headings in the file.
File not saving as Markdown?
Make sure the file extension is .md and not .txt. Some editors default to plain text. In VS Code, use File > Save As and type the full filename including the .md extension.
If you're using Notepad on Windows, change the Save as type dropdown to All Files before saving.
Help me save my file as Markdown
✔️ Awesome, I've got everything!
Great. Make sure you've saved the file before moving on.
ⓧ I'd like to double check the full code
Here's what your complete url-shortener-design.md file should look like at this point:
# URL Shortener System Design
## 1. Requirements
## 2. Estimation (Back-of-Envelope)
## 3. Storage
## 4. High-Level Design
## 5. APIs
## 6. Deep Dive: Key Generation Strategy
## 7. Evaluation
Save the Excalidraw canvas to disk
Saving your Excalidraw canvas creates a local file you can reopen and edit later. This is your architecture diagram file.
- Switch back to your Excalidraw tab in the browser.
- Click the hamburger menu icon in the top-left corner of the canvas.
- Select Save to disk.
- Your browser will prompt you to choose a save location. Save the file to your Desktop as url-shortener-architecture.excalidraw.
Why save to disk?
Excalidraw runs entirely in your browser with no account needed. Saving to disk creates a local .excalidraw file that you can reopen at any time by dragging it back into excalidraw.com.
- Verify both files are on your Desktop by checking for url-shortener-design.md and url-shortener-architecture.excalidraw in Finder (macOS) or File Explorer (Windows).
Don't see the .excalidraw file?
Check your browser's default downloads folder. Some browsers save files there instead of the location you chose. Move the file to your Desktop if needed.
If the save option appears grayed out, try pressing Cmd+S (macOS) or Ctrl+S (Windows) instead.
Help me save my Excalidraw file to disk
Your workspace is ready. Next up, you'll fill in the Requirements and Estimation sections with the kind of structured reasoning that scores highest in interviews.
Define Requirements and Capacity Estimation
Your design workspace is ready. Now it's time to fill in the first two sections of your RESHADED framework: Requirements and Estimation.
These two sections demonstrate scope control. Requirements show you can ask the right clarifying questions before jumping into architecture. Estimation shows you can reason quantitatively about scale. Together, they anchor every decision you make in the rest of the design.
In this step, get ready to:
- Fill in functional and non-functional requirements for the URL shortener.
- Calculate back-of-envelope capacity estimates (QPS, storage, bandwidth, cache).
- Document your assumptions explicitly so an interviewer sees deliberate decision-making.
Fill in the requirements section
Every system design starts with scoping. You need to separate what the system does (functional requirements) from how well it does it (non-functional requirements). In an interview, this is also where you state assumptions to show you can reason about constraints before diving into architecture.
- In your text editor, find the ## 1. Requirements heading in url-shortener-design.md.
- Place your cursor on the empty line below that heading and paste the following content:
### Functional Requirements
- Users can submit a long URL and receive a shortened URL
- Users clicking a short URL are redirected to the original long URL
- Users can optionally specify a custom alias for the short URL
- URLs can have an optional expiration time
- Short codes are 7 characters, Base62 encoded (a-z, A-Z, 0-9)
### Non-Functional Requirements
- High availability: the system must be always accessible (99.9% uptime)
- Low latency: redirects must complete in under 100ms
- Scale: 100 million new URLs created per month
- Short URLs should not be predictable (security)
### Assumptions and Clarifications
- Read-to-write ratio: 100:1 (redirects vastly outnumber URL creations)
- Data retention: 5 years before cleanup
- No authentication required for creating short URLs (simplification)
---
What does this content cover?
Your requirements section has three distinct parts that map to how interviewers evaluate you.
- Functional requirements define the core user actions: creating a short URL, redirecting, custom aliases, and expiration.
- Non-functional requirements define quality attributes: 99.9% uptime, sub-100ms latency, and scale targets.
- Assumptions and clarifications state what you decided upfront. The 100:1 read-to-write ratio is the most important one because it drives every capacity number that follows.
Why state a read-to-write ratio?
URL shorteners receive far more redirect requests (reads) than URL creation requests (writes). Stating this ratio explicitly shows the interviewer you understand the system's dominant workload. It also determines whether you optimize for read performance or write throughput in your architecture.
- Save url-shortener-design.md.
- Scroll through your Requirements section. You should see three subsections with bullet points under each: Functional Requirements (5 items), Non-Functional Requirements (4 items), and Assumptions and Clarifications (3 items).
Formatting looks broken?
- Make sure you pasted the content on the line directly below ## 1. Requirements. If there is no blank line between the heading and the first ### subheading, add one.
- Check that each ### has a space after it. Missing spaces break Markdown heading rendering.
- Confirm the --- separator is on its own line with a blank line above it.
Help me fix my Markdown formatting in the Requirements section.
Calculate your capacity estimates
Back-of-envelope math proves you can think about scale without needing a calculator or exact figures. The technique starts with one known number (100M URLs/month) and derives everything else from it.
- In url-shortener-design.md, find the ## 2. Estimation (Back-of-Envelope) heading.
- Place your cursor on the empty line below that heading and paste the following content:
### Write traffic
- 100M new URLs / month
- 100M / (30 days x 24 hrs x 3600 sec) = ~40 writes/second
### Read traffic
- 100:1 read-to-write ratio
- 40 x 100 = ~4,000 reads/second (redirects)
### Storage
- Each URL mapping: ~500 bytes (short_code + long_url + metadata)
- 5 years of data: 100M/month x 12 x 5 = 6 billion records
- Total storage: 6B x 500 bytes = ~3 TB
### Bandwidth
- Outbound: 4,000 reads/sec x 500 bytes = ~2 MB/s
- Manageable for modern infrastructure
### Cache
- Following 80/20 rule: 20% of URLs generate 80% of traffic
- Daily read requests: 4,000/sec x 86,400 sec = ~346M reads/day
- Cache 20% of daily reads: ~35GB of cache needed (fits in Redis)
---
How does this math chain together?
Every number derives from the one before it. This is the skill interviewers are testing.
- Start with 100M URLs/month. Divide by seconds-in-a-month to get ~40 writes/sec.
- Multiply by the 100:1 ratio to get ~4,000 reads/sec. This tells you the system is read-heavy.
- Multiply records by size (~500 bytes each) over 5 years to get ~3 TB total storage.
- Multiply reads/sec by record size to get ~2 MB/s bandwidth. This is low, so bandwidth is not the bottleneck.
- Apply the 80/20 rule to size your cache. 20% of daily reads at ~500 bytes each gives ~35GB, which fits comfortably in Redis.
- Save url-shortener-design.md.
- Scroll through your Estimation section. You should see five subsections: Write traffic, Read traffic, Storage, Bandwidth, and Cache. Each has concrete numbers derived from the previous calculation.
Numbers not matching up?
- Double-check the seconds-in-a-month calculation: 30 x 24 x 3600 = 2,592,000. Dividing 100M by this gives approximately 40.
- The storage calculation multiplies 100M/month by 60 months (5 years) to get 6 billion records, then multiplies by 500 bytes.
- If your file shows the content but the Markdown looks raw, make sure your editor is rendering Markdown preview.
Walk me through the capacity estimation math step by step.
✔️ Awesome, I've got everything!
Great. Double check you've saved your file with both sections filled in.
ⓧ I'd like to double check the full code
Here is the full url-shortener-design.md file as it should look after this step:
# URL Shortener System Design
## 1. Requirements
### Functional Requirements
- Users can submit a long URL and receive a shortened URL
- Users clicking a short URL are redirected to the original long URL
- Users can optionally specify a custom alias for the short URL
- URLs can have an optional expiration time
- Short codes are 7 characters, Base62 encoded (a-z, A-Z, 0-9)
### Non-Functional Requirements
- High availability: the system must be always accessible (99.9% uptime)
- Low latency: redirects must complete in under 100ms
- Scale: 100 million new URLs created per month
- Short URLs should not be predictable (security)
### Assumptions and Clarifications
- Read-to-write ratio: 100:1 (redirects vastly outnumber URL creations)
- Data retention: 5 years before cleanup
- No authentication required for creating short URLs (simplification)
---
## 2. Estimation (Back-of-Envelope)
### Write traffic
- 100M new URLs / month
- 100M / (30 days x 24 hrs x 3600 sec) = ~40 writes/second
### Read traffic
- 100:1 read-to-write ratio
- 40 x 100 = ~4,000 reads/second (redirects)
### Storage
- Each URL mapping: ~500 bytes (short_code + long_url + metadata)
- 5 years of data: 100M/month x 12 x 5 = 6 billion records
- Total storage: 6B x 500 bytes = ~3 TB
### Bandwidth
- Outbound: 4,000 reads/sec x 500 bytes = ~2 MB/s
- Manageable for modern infrastructure
### Cache
- Following 80/20 rule: 20% of URLs generate 80% of traffic
- Daily read requests: 4,000/sec x 86,400 sec = ~346M reads/day
- Cache 20% of daily reads: ~35GB of cache needed (fits in Redis)
---
## 3. Storage
## 4. High-Level Design
## 5. APIs
## 6. Deep Dive: Key Generation Strategy
## 7. Evaluation
Review and justify your assumptions
Look back at your Assumptions and Clarifications subsection. You stated three decisions upfront: the 100:1 ratio, 5-year retention, and no authentication. In an interview, you would say these out loud before starting the math.
Why do interviewers care about explicit assumptions?
Interviewers care that you made a decision and can justify it. They do not care that your numbers are exact. Two candidates can assume different ratios (50:1 vs 100:1) and both score equally well, as long as each candidate explains their reasoning.
The 100:1 read-to-write ratio is the most impactful assumption here. It means your architecture must prioritize read performance (caching, replicas, CDN) over write throughput. If you stated 10:1 instead, your design would look different. That traceability from assumption to decision is what scores points.
Practice saying it out loud
Read your Requirements and Estimation sections aloud as if presenting to an interviewer. In a real 45-minute interview, you have about 5 minutes for requirements and 5 minutes for estimation. If you can walk through both in under 10 minutes total, you are on pace.
Your requirements and capacity numbers are locked in. Next up, you will design the database schema and draw the high-level architecture diagram that brings these numbers to life.
Design Storage Schema and High-Level Architecture
You now have concrete numbers for your system's traffic, storage, and bandwidth. Those numbers tell you what your infrastructure needs to handle. The next question is: how do you actually store and serve 6 billion URL mappings at 4,000 reads per second?
In this step, you will design your database schema, choose between SQL and NoSQL (and justify that choice), and then draw the full high-level architecture diagram in Excalidraw. This is where your system starts to take visual shape.
In this step, get ready to:
- Design a database schema and justify your NoSQL choice with a concrete trade-off statement.
- Draw the high-level architecture diagram in Excalidraw with labeled components and data flow arrows.
- Document each component's role and the read/write data flow paths.
Design the database schema and choose your database
Every system design interview expects you to define what your data looks like and where it lives. The schema should flow directly from your functional requirements.
- In url-shortener-design.md, find the empty ## 3. Storage section.
- Replace the empty section with the following schema and database choice content:
## 3. Storage
### Database Schema
| Column | Type | Notes |
|-------------|----------------|-----------------------------||
| short_code | VARCHAR(7) | Primary key |
| long_url | VARCHAR(2048) | Original URL |
| created_at | TIMESTAMP | Creation time |
| expires_at | TIMESTAMP | Nullable, for URL expiration|
| user_id | VARCHAR(64) | Nullable, optional owner |
### Database Choice: NoSQL Key-Value Store
**Decision:** Use a NoSQL key-value store (e.g., DynamoDB or Cassandra)
**Trade-off:**
- SQL gives us transactions, ACID compliance, and rich querying. But we don't need joins, complex queries, or multi-table transactions. Our access pattern is a simple key lookup.
- NoSQL gives us horizontal scaling out of the box, simple key-value access that matches our pattern exactly, and better write throughput at scale.
- At 3TB+ of data with simple lookup patterns, NoSQL is the right fit.
**What we give up:** No ad-hoc SQL queries for analytics (solved later with a separate analytics store).
Why NoSQL over SQL here?
The key insight is that your access pattern is a simple key-value lookup: given a short_code, return the long_url. You never need to join tables, run complex aggregation queries, or maintain relational integrity across multiple entities.
Interviewers care that you match your database choice to your access pattern. Stating the trade-off explicitly (what you gain AND what you give up) is what separates an L4 answer from an L5 answer.
- Save url-shortener-design.md.
- Scroll through your Storage section. You should see a five-column schema table followed by a "Database Choice" subsection with your trade-off reasoning.
Table not rendering in your editor's preview?
Make sure the pipe characters | align in each row and that the separator row uses dashes --- between each pipe. Markdown tables break if any row has a different number of columns.
Help me fix my Markdown table formatting
Draw the high-level architecture in Excalidraw
Your architecture diagram is what you would draw on a whiteboard in an actual interview. It shows the interviewer you can think in systems, not just components.
- Switch to your Excalidraw canvas in your browser.
- Select the rectangle tool from the toolbar at the top (or press R).
- Draw five rectangles spread across the canvas from left to right, and label each one by double-clicking it:
- Client (far left).
- Load Balancer (left of center).
- Application Servers (center). Draw two or three stacked rectangles to represent horizontal scaling.
- Cache (Redis) (right of center).
- Database (NoSQL) (far right).
- Select the arrow tool (or press A) and draw arrows connecting the components from left to right.
- Label each arrow by double-clicking on it. Add these labels:
- POST /shorten on the arrow from Client to Load Balancer.
- GET /{shortCode} on a second arrow from Client to Load Balancer.
- An arrow from Application Servers to Cache (Redis) labeled cache hit.
- An arrow from Application Servers to Database (NoSQL) labeled cache miss.
- An arrow from Application Servers back toward Client labeled redirect 301.
- Save your diagram by pressing Ctrl+S (Windows) or Cmd+S (macOS), or use the hamburger menu and select Save to disk.
Your canvas should show five labeled components connected by arrows that clearly communicate two paths: the write path (shorten a URL) and the read path (redirect a short URL).
Diagram clarity over beauty
In a real interview, your whiteboard diagram will never be pixel-perfect. Interviewers evaluate whether they can follow the data flow from client to response. Make sure your arrow labels are readable and the left-to-right flow is obvious.
Arrows connecting to wrong components?
Click on the arrow, then drag its endpoints to snap to the correct rectangle. In Excalidraw, arrows snap to component edges when you drag close enough.
Help me arrange my architecture diagram in Excalidraw
Document the high-level design
Your diagram shows the architecture visually. Your document needs to describe each component's role and the two data flow paths in words. This written version is what gives an interviewer confidence you understand why each piece exists.
- Return to url-shortener-design.md in your text editor.
- Find the empty ## 4. High-Level Design section.
- Replace it with the following component descriptions and data flow documentation:
## 4. High-Level Design
### Components
1. **Load Balancer** - Distributes incoming requests across application servers. Health checks route traffic away from failed servers.
2. **Application Servers** (horizontally scaled) - Handle business logic:
- Write path: receive long URL, request a key from Key Generation Service, store mapping, return short URL
- Read path: receive short code, check cache, if miss check database, return 301 redirect
3. **Cache Layer (Redis)** - Stores frequently accessed URL mappings in memory. LRU eviction. Reduces database load for popular URLs.
4. **Database (NoSQL)** - Durable storage for all URL mappings. Horizontally sharded for scale.
5. **Key Generation Service** - Pre-generates unique 7-character Base62 keys. App servers request unused keys from the pool. Eliminates collision handling at URL creation time.
### Data Flow
**Write path:**
Client -> Load Balancer -> App Server -> Key Generation Service (get key) -> Database (store mapping) -> Return short URL
**Read path:**
Client -> Load Balancer -> App Server -> Cache (hit?) -> Database (miss) -> Return 301 Redirect
What does each component do?
- Load Balancer spreads traffic across multiple app servers so no single server gets overwhelmed. It also detects failed servers and stops sending them traffic.
- Cache Layer (Redis) keeps the most popular URLs in memory. Since 20% of URLs drive 80% of traffic, caching avoids hitting the database for most reads.
- Key Generation Service is introduced here even though you will deep-dive into it in the next step. Mentioning it now shows the interviewer you are thinking ahead.
- Save url-shortener-design.md.
- Scroll through your High-Level Design section. You should see five numbered components with descriptions, followed by Write path and Read path summaries that trace data from Client all the way through and back.
Not sure if your data flow matches your diagram?
Compare your Excalidraw arrows with the text paths in your document. Every arrow label in the diagram should correspond to a step in the written data flow. If they disagree, update whichever is incomplete.
Help me verify my architecture diagram matches my written design
✔️ Awesome, I've got everything!
Great. Double check you have saved both url-shortener-design.md and your Excalidraw diagram.
ⓧ I'd like to double check the full code
Here is the full url-shortener-design.md as it should look at the end of this step (Sections 1-4 filled in, 5-7 still empty headings):
# URL Shortener System Design
## 1. Requirements
### Functional Requirements
- Users can submit a long URL and receive a shortened URL
- Users clicking a short URL are redirected to the original long URL
- Users can optionally specify a custom alias for the short URL
- URLs can have an optional expiration time
- Short codes are 7 characters, Base62 encoded (a-z, A-Z, 0-9)
### Non-Functional Requirements
- High availability: the system must be always accessible (99.9% uptime)
- Low latency: redirects must complete in under 100ms
- Scale: 100 million new URLs created per month
- Short URLs should not be predictable (security)
### Assumptions and Clarifications
- Read-to-write ratio: 100:1 (redirects vastly outnumber URL creations)
- Data retention: 5 years before cleanup
- No authentication required for creating short URLs (simplification)
---
## 2. Estimation (Back-of-Envelope)
### Write traffic
- 100M new URLs / month
- 100M / (30 days x 24 hrs x 3600 sec) = ~40 writes/second
### Read traffic
- 100:1 read-to-write ratio
- 40 x 100 = ~4,000 reads/second (redirects)
### Storage
- Each URL mapping: ~500 bytes (short_code + long_url + metadata)
- 5 years of data: 100M/month x 12 x 5 = 6 billion records
- Total storage: 6B x 500 bytes = ~3 TB
### Bandwidth
- Outbound: 4,000 reads/sec x 500 bytes = ~2 MB/s
- Manageable for modern infrastructure
### Cache
- Following 80/20 rule: 20% of URLs generate 80% of traffic
- Daily read requests: 4,000/sec x 86,400 sec = ~346M reads/day
- Cache 20% of daily reads: ~35GB of cache needed (fits in Redis)
---
## 3. Storage
### Database Schema
| Column | Type | Notes |
|-------------|----------------|-----------------------------||
| short_code | VARCHAR(7) | Primary key |
| long_url | VARCHAR(2048) | Original URL |
| created_at | TIMESTAMP | Creation time |
| expires_at | TIMESTAMP | Nullable, for URL expiration|
| user_id | VARCHAR(64) | Nullable, optional owner |
### Database Choice: NoSQL Key-Value Store
**Decision:** Use a NoSQL key-value store (e.g., DynamoDB or Cassandra)
**Trade-off:**
- SQL gives us transactions, ACID compliance, and rich querying. But we don't need joins, complex queries, or multi-table transactions. Our access pattern is a simple key lookup.
- NoSQL gives us horizontal scaling out of the box, simple key-value access that matches our pattern exactly, and better write throughput at scale.
- At 3TB+ of data with simple lookup patterns, NoSQL is the right fit.
**What we give up:** No ad-hoc SQL queries for analytics (solved later with a separate analytics store).
---
## 4. High-Level Design
### Components
1. **Load Balancer** - Distributes incoming requests across application servers. Health checks route traffic away from failed servers.
2. **Application Servers** (horizontally scaled) - Handle business logic:
- Write path: receive long URL, request a key from Key Generation Service, store mapping, return short URL
- Read path: receive short code, check cache, if miss check database, return 301 redirect
3. **Cache Layer (Redis)** - Stores frequently accessed URL mappings in memory. LRU eviction. Reduces database load for popular URLs.
4. **Database (NoSQL)** - Durable storage for all URL mappings. Horizontally sharded for scale.
5. **Key Generation Service** - Pre-generates unique 7-character Base62 keys. App servers request unused keys from the pool. Eliminates collision handling at URL creation time.
### Data Flow
**Write path:**
Client -> Load Balancer -> App Server -> Key Generation Service (get key) -> Database (store mapping) -> Return short URL
**Read path:**
Client -> Load Balancer -> App Server -> Cache (hit?) -> Database (miss) -> Return 301 Redirect
---
## 5. APIs
## 6. Deep Dive: Key Generation Strategy
## 7. Evaluation
Your system now has a shape. You have a schema, a justified database choice, and a full architecture diagram showing how data flows through the system. Next up, you will design the API endpoints and deep-dive into the hardest part of this system: how to generate billions of unique short codes without collisions.
Design APIs and Deep Dive on Key Generation
Your architecture diagram and storage design give interviewers the structural picture. Now you need to show them the two things that separate a senior candidate from a mid-level one: precise API contracts and deep technical reasoning on the hardest subproblem.
In a real interview, this is the 20-35 minute mark where you shift from broad design to focused depth. The "hard part" for a URL shortener is key generation. Interviewers want to see that you can identify multiple approaches, articulate trade-offs for each, and make a justified decision.
In this step, get ready to:
- Design two API endpoints with full request/response specs and error codes.
- Analyze three key generation strategies and justify your choice with concrete trade-offs.
- Update your Excalidraw diagram with the Key Generation Service and Key Pool DB.
Fill in the API endpoints
API design in a system design interview demonstrates that you can translate high-level architecture into concrete contracts. Interviewers look for appropriate HTTP methods, meaningful status codes, and awareness of redirect semantics.
- In url-shortener-design.md, find the empty ## 5. APIs section heading.
- Paste the following content directly below that heading:
### Create Short URL
```
POST /api/v1/shorten
Request Body:
{
"long_url": "https://www.example.com/very/long/path/to/resource",
"custom_alias": "my-link", // optional
"expires_at": "2027-01-01T00:00:00Z" // optional
}
Response (201 Created):
{
"short_url": "https://short.ly/abc1234",
"short_code": "abc1234",
"long_url": "https://www.example.com/very/long/path/to/resource",
"expires_at": "2027-01-01T00:00:00Z"
}
Errors:
- 400: Invalid URL format
- 409: Custom alias already taken
- 429: Rate limit exceeded
```
### Redirect
```
GET /{shortCode}
Response: 301 Moved Permanently
Location: https://www.example.com/very/long/path/to/resource
Errors:
- 404: Short code not found
- 410: URL has expired
```
### 301 vs 302 Trade-off
- **301 (Permanent Redirect):** Browser caches the redirect. Reduces load on our servers for repeat visits. But makes analytics harder because repeat visits bypass our servers entirely.
- **302 (Temporary Redirect):** Browser always hits our server. Better for analytics. But higher server load.
- **Decision:** Use 301 for performance. Solve analytics separately via async event tracking on first redirect.
What does this API design cover?
You have defined two endpoints that cover the entire user-facing surface of the system.
- POST /api/v1/shorten handles URL creation with optional custom aliases and expiration. The 201, 400, 409, and 429 status codes show you've thought about validation, conflicts, and rate limiting.
- GET /{shortCode} handles the redirect. The 301 response tells browsers to cache the redirect permanently.
- The 301 vs 302 trade-off shows the interviewer you understand the downstream implications of an HTTP status code choice on both performance and analytics.
Why 301 over 302?
A 301 (Permanent Redirect) tells the browser to cache the mapping. Repeat visitors never hit your servers again for that URL. This slashes read load at the cost of making click analytics harder.
A 302 (Temporary Redirect) forces every click through your servers. Better for analytics, but your infrastructure handles 100% of repeat traffic. Choosing 301 and solving analytics separately (via async event tracking) is the stronger answer because it optimizes for the dominant workload.
- Save url-shortener-design.md.
- Scroll through your Section 5 and confirm you see both endpoint specs with request/response bodies and the 301 vs 302 Trade-off subsection at the bottom.
Analyze three key generation approaches
This is the "hard part" interviewers want you to go deep on. Key generation is where most URL shortener designs either shine or collapse. Presenting multiple approaches with explicit pros and cons, then making a justified choice, is exactly what earns top marks on the trade-off reasoning dimension.
- In url-shortener-design.md, find the empty ## 6. Deep Dive: Key Generation Strategy section heading.
- Paste the following content directly below that heading:
### The Problem
We need to generate unique 7-character codes for billions of URLs without collisions.
7 chars in Base62 = 62^7 = ~3.5 trillion possible codes. More than enough for our 6B URL estimate.
### Three Approaches
#### Approach A: Hash and Truncate
- Take MD5 or SHA-256 of the long URL, encode in Base62, take first 7 chars.
- **Pro:** Deterministic. Same long URL always produces the same short code (deduplication for free).
- **Con:** Collisions are likely at scale (birthday problem). Need collision resolution logic (retry with salt). Adds latency to the write path.
#### Approach B: Auto-Incrementing Counter + Base62
- A central counter service issues sequential IDs. Convert to Base62.
- **Pro:** Zero collisions guaranteed. Simple.
- **Con:** Single point of failure (the counter). Sequential codes are guessable (security risk: enumerate all URLs). Scaling the counter across regions is hard.
#### Approach C: Pre-Generated Key Pool (CHOSEN)
- A background service pre-generates random 7-char Base62 strings and stores them in a "key pool" database. App servers pull an unused key when creating a new short URL. Mark key as used.
- **Pro:** No collision handling needed. O(1) key acquisition time. Keys are random (not guessable). Decoupled from the hot path.
- **Con:** Requires a separate key pool service. Pool must be sized to never run out. Coordination needed to prevent two servers grabbing the same key (solved with atomic operations or key-range assignment).
### Why Approach C
- Eliminates collision handling from the write path entirely
- Random keys provide better security than sequential ones
- The key pool can be pre-sized (generate 1M keys ahead of time) so the pool never runs out under normal load
- Multiple app servers can be assigned non-overlapping key ranges to eliminate coordination overhead
What makes this deep dive interview-ready?
You framed the problem first (3.5 trillion possible codes vs 6B needed), then presented three distinct approaches. Each approach has explicit pros and cons that reference concrete system properties (latency, collision rate, security, single point of failure).
The choice of Approach C (pre-generated key pool) is justified by four reasons. In an interview, you would walk through all three approaches in about 3 minutes, then spend 2 minutes on why you picked C. This structure maps directly to the "Detailed Design" scoring dimension (35% weight) in FAANG rubrics.
Practice the 60-second explanation
Read your "Why Approach C" section aloud. Can you explain all four reasons in under 60 seconds without reading? If yes, you are at interview speed. If not, practice until the reasoning flows naturally.
- Save url-shortener-design.md.
- Scroll through Section 6 and confirm you see all three approaches (Hash and Truncate, Auto-Incrementing Counter, Pre-Generated Key Pool) with their pros and cons, plus the "Why Approach C" justification at the end.
✔️ Awesome, I've got everything!
Great. Make sure you have saved your file before continuing.
ⓧ I'd like to double check the full code
Here is the full url-shortener-design.md file up to and including Section 6:
# URL Shortener System Design
## 1. Requirements
### Functional Requirements
- Users can submit a long URL and receive a shortened URL
- Users clicking a short URL are redirected to the original long URL
- Users can optionally specify a custom alias for the short URL
- URLs can have an optional expiration time
- Short codes are 7 characters, Base62 encoded (a-z, A-Z, 0-9)
### Non-Functional Requirements
- High availability: the system must be always accessible (99.9% uptime)
- Low latency: redirects must complete in under 100ms
- Scale: 100 million new URLs created per month
- Short URLs should not be predictable (security)
### Assumptions and Clarifications
- Read-to-write ratio: 100:1 (redirects vastly outnumber URL creations)
- Data retention: 5 years before cleanup
- No authentication required for creating short URLs (simplification)
---
## 2. Estimation (Back-of-Envelope)
### Write traffic
- 100M new URLs / month
- 100M / (30 days x 24 hrs x 3600 sec) = ~40 writes/second
### Read traffic
- 100:1 read-to-write ratio
- 40 x 100 = ~4,000 reads/second (redirects)
### Storage
- Each URL mapping: ~500 bytes (short_code + long_url + metadata)
- 5 years of data: 100M/month x 12 x 5 = 6 billion records
- Total storage: 6B x 500 bytes = ~3 TB
### Bandwidth
- Outbound: 4,000 reads/sec x 500 bytes = ~2 MB/s
- Manageable for modern infrastructure
### Cache
- Following 80/20 rule: 20% of URLs generate 80% of traffic
- Daily read requests: 4,000/sec x 86,400 sec = ~346M reads/day
- Cache 20% of daily reads: ~35GB of cache needed (fits in Redis)
---
## 3. Storage
### Database Schema
| Column | Type | Notes |
|-------------|----------------|-----------------------------|
| short_code | VARCHAR(7) | Primary key |
| long_url | VARCHAR(2048) | Original URL |
| created_at | TIMESTAMP | Creation time |
| expires_at | TIMESTAMP | Nullable, for URL expiration|
| user_id | VARCHAR(64) | Nullable, optional owner |
### Database Choice: NoSQL Key-Value Store
**Decision:** Use a NoSQL key-value store (e.g., DynamoDB or Cassandra)
**Trade-off:**
- SQL gives us transactions, ACID compliance, and rich querying. But we don't need joins, complex queries, or multi-table transactions. Our access pattern is a simple key lookup.
- NoSQL gives us horizontal scaling out of the box, simple key-value access that matches our pattern exactly, and better write throughput at scale.
- At 3TB+ of data with simple lookup patterns, NoSQL is the right fit.
**What we give up:** No ad-hoc SQL queries for analytics (solved later with a separate analytics store).
---
## 4. High-Level Design
### Components
1. **Load Balancer** - Distributes incoming requests across application servers. Health checks route traffic away from failed servers.
2. **Application Servers** (horizontally scaled) - Handle business logic:
- Write path: receive long URL, request a key from Key Generation Service, store mapping, return short URL
- Read path: receive short code, check cache, if miss check database, return 301 redirect
3. **Cache Layer (Redis)** - Stores frequently accessed URL mappings in memory. LRU eviction. Reduces database load for popular URLs.
4. **Database (NoSQL)** - Durable storage for all URL mappings. Horizontally sharded for scale.
5. **Key Generation Service** - Pre-generates unique 7-character Base62 keys. App servers request unused keys from the pool. Eliminates collision handling at URL creation time.
### Data Flow
**Write path:**
Client -> Load Balancer -> App Server -> Key Generation Service (get key) -> Database (store mapping) -> Return short URL
**Read path:**
Client -> Load Balancer -> App Server -> Cache (hit?) -> Database (miss) -> Return 301 Redirect
---
## 5. APIs
### Create Short URL
```
POST /api/v1/shorten
Request Body:
{
"long_url": "https://www.example.com/very/long/path/to/resource",
"custom_alias": "my-link", // optional
"expires_at": "2027-01-01T00:00:00Z" // optional
}
Response (201 Created):
{
"short_url": "https://short.ly/abc1234",
"short_code": "abc1234",
"long_url": "https://www.example.com/very/long/path/to/resource",
"expires_at": "2027-01-01T00:00:00Z"
}
Errors:
- 400: Invalid URL format
- 409: Custom alias already taken
- 429: Rate limit exceeded
```
### Redirect
```
GET /{shortCode}
Response: 301 Moved Permanently
Location: https://www.example.com/very/long/path/to/resource
Errors:
- 404: Short code not found
- 410: URL has expired
```
### 301 vs 302 Trade-off
- **301 (Permanent Redirect):** Browser caches the redirect. Reduces load on our servers for repeat visits. But makes analytics harder because repeat visits bypass our servers entirely.
- **302 (Temporary Redirect):** Browser always hits our server. Better for analytics. But higher server load.
- **Decision:** Use 301 for performance. Solve analytics separately via async event tracking on first redirect.
---
## 6. Deep Dive: Key Generation Strategy
### The Problem
We need to generate unique 7-character codes for billions of URLs without collisions.
7 chars in Base62 = 62^7 = ~3.5 trillion possible codes. More than enough for our 6B URL estimate.
### Three Approaches
#### Approach A: Hash and Truncate
- Take MD5 or SHA-256 of the long URL, encode in Base62, take first 7 chars.
- **Pro:** Deterministic. Same long URL always produces the same short code (deduplication for free).
- **Con:** Collisions are likely at scale (birthday problem). Need collision resolution logic (retry with salt). Adds latency to the write path.
#### Approach B: Auto-Incrementing Counter + Base62
- A central counter service issues sequential IDs. Convert to Base62.
- **Pro:** Zero collisions guaranteed. Simple.
- **Con:** Single point of failure (the counter). Sequential codes are guessable (security risk: enumerate all URLs). Scaling the counter across regions is hard.
#### Approach C: Pre-Generated Key Pool (CHOSEN)
- A background service pre-generates random 7-char Base62 strings and stores them in a "key pool" database. App servers pull an unused key when creating a new short URL. Mark key as used.
- **Pro:** No collision handling needed. O(1) key acquisition time. Keys are random (not guessable). Decoupled from the hot path.
- **Con:** Requires a separate key pool service. Pool must be sized to never run out. Coordination needed to prevent two servers grabbing the same key (solved with atomic operations or key-range assignment).
### Why Approach C
- Eliminates collision handling from the write path entirely
- Random keys provide better security than sequential ones
- The key pool can be pre-sized (generate 1M keys ahead of time) so the pool never runs out under normal load
- Multiple app servers can be assigned non-overlapping key ranges to eliminate coordination overhead
---
## 7. Evaluation
Add the Key Generation Service to your diagram
Your architecture diagram from Step 3 shows the high-level flow but is missing the key generation detail you just wrote about. Adding it to the diagram proves to interviewers that your deep dive connects back to your architecture.
- Switch back to your Excalidraw canvas.
- Draw a new rectangle to the right of or below your Application Servers. Label it Key Generation Service.
- Draw a second, smaller rectangle connected to the Key Generation Service. Label it Key Pool DB.
- Draw an arrow from your Application Servers to the Key Generation Service. Label it get key.
- Draw an arrow from the Key Generation Service to the Key Pool DB. Label it fetch/mark used.
- Save your diagram by pressing Cmd+S (macOS) or Ctrl+S (Windows) to save locally as url-shortener-architecture.excalidraw.
What does this addition show?
The Key Generation Service sits between the App Servers and the Key Pool DB. On the write path, the App Server requests a pre-generated key, the service fetches one and marks it as used, then the App Server pairs the key with the long URL and stores the mapping in the main database.
This visual separation makes it clear that key generation is decoupled from URL storage. The read path (redirects) never touches the Key Generation Service at all.
- Verify your canvas now shows all of these components: Client, Load Balancer, Application Servers, Cache (Redis), Database (NoSQL), Key Generation Service, and Key Pool DB.
- Confirm the arrows clearly show the write path flowing through the Key Generation Service before reaching the Database.
Diagram looking cluttered?
If your components are overlapping, select all elements (Cmd+A or Ctrl+A) and spread them out. Keep the read path (top or left) visually separate from the write path (bottom or right) so the two flows don't cross.
help me lay out my system design diagram so the read and write paths are visually distinct
You now have a fully specified API layer and a deep-dive section that demonstrates exactly the kind of reasoning interviewers score highest on. Next up, you will complete the final section: trade-off evaluation, failure mode analysis, and cost reasoning.
Analyze Trade-offs, Failure Modes, and Cost
Your design document now has a complete architecture, API specification, and deep dive on key generation. An interviewer reading your document so far would see solid technical thinking.
But the highest-scoring section is still empty. Trade-off analysis is the single most differentiating criterion in system design interviews. Two candidates with identical architectures score differently based on whether they explicitly state what they chose, what they chose it over, and why.
In this step, you will fill in Section 7 (Evaluation) with structured trade-offs, a failure mode analysis for every component, and a cost reasoning paragraph. This is where your design goes from "technically correct" to "senior engineer thinking."
In this step, get ready to:
- Write a trade-off table covering 5 architectural decisions with alternatives and justifications.
- Document failure modes for every major component with recovery strategies.
- Add cost reasoning that identifies dominant costs and strategies to reduce them.
Write the trade-off analysis table
Every architectural decision you made earlier had an alternative you rejected. Interviewers want to see that you considered alternatives and can articulate why you picked one over the other in a structured way.
You already documented three trade-offs throughout your design (NoSQL vs SQL, 301 vs 302, key generation approach). Now you will consolidate those into a single table and add two new ones: cache eviction policy and data partitioning strategy.
- In url-shortener-design.md, find the empty ## 7. Evaluation section at the bottom of your file.
- Add the following trade-offs table below the ## 7. Evaluation heading:
### Trade-offs Made
| Decision | Chose | Over | Reason |
|----------|-------|------|--------|
| Database | NoSQL key-value | SQL/relational | Simple key lookup pattern, horizontal scaling at 3TB+ |
| Redirect code | 301 (permanent) | 302 (temporary) | Reduces server load, analytics solved separately |
| Key generation | Pre-generated pool | Hash/Counter | No collisions, O(1), random keys |
| Cache eviction | LRU | TTL-based | Keeps popular URLs hot; newly created URLs may get evicted before going viral, but viral URLs quickly re-enter cache |
| Partitioning | Consistent hashing | Range-based | Even distribution, no hot spots on popular prefixes |
What does this table communicate?
Each row follows the same pattern: what you chose, what you chose it over, and a one-sentence justification. This format makes it trivial for an interviewer to verify your reasoning.
The two new trade-offs (cache eviction and partitioning) show you think beyond the "obvious" decisions. LRU keeps popular URLs hot but might evict fresh URLs before they go viral. Consistent hashing distributes data evenly but adds lookup complexity compared to simple range-based sharding.
- Save url-shortener-design.md.
- Verify you have exactly 5 rows in your trade-offs table, each with all four columns filled in.
Practice verbalizing each row
In a real interview, you would explain each trade-off verbally in under 60 seconds. Try reading each row aloud and expanding it into 2-3 spoken sentences. If you can do that fluently, you are ready for the trade-off portion of the interview.
Document failure modes for each component
Interviewers increasingly expect candidates to reason about what happens when things break. For each component in your architecture, you need to explain what goes wrong and how the system recovers without manual intervention.
- Add the following failure modes table below your trade-offs table in url-shortener-design.md:
### Failure Modes
| Component | Failure Impact | Recovery Strategy |
|-----------|---------------|-------------------|
| Load Balancer | All traffic drops | DNS failover to standby LB; health monitoring triggers automatic promotion |
| App Server (one) | Reduced capacity | LB health checks detect failure in seconds, route traffic to healthy servers |
| Cache (Redis) | Higher DB latency | Requests fall through to database. Not an outage, but P99 latency spikes. Redis Cluster provides replication |
| Database | Data unavailable | Read replicas promoted for reads. Write downtime until primary recovers or failover completes |
| Key Pool exhausted | Cannot create new URLs | Alert fires when pool drops below threshold. Background service replenishes. Existing redirects unaffected |
Why failure modes matter in interviews
Notice the pattern in the Recovery Strategy column. Every recovery is automatic, not "call an engineer at 3am." This demonstrates operational maturity.
The key insight is that cache failure is a latency degradation, not a full outage. Redis going down means requests fall through to the database. The system still works, just slower. Interviewers love when you distinguish between "degraded" and "down."
- Save url-shortener-design.md.
- Verify you have 5 rows in your failure modes table, one for each major component in your Excalidraw diagram.
Not sure what "DNS failover" means?
DNS failover means that when the primary load balancer stops responding, the DNS record automatically updates to point to a standby load balancer. The client never sees the switch because it happens at the network level.
Help me understand DNS failover and how it protects against load balancer failure.
Add cost reasoning
Cost reasoning is a 2026 rubric addition that most candidates miss. Senior-level candidates are expected to identify what drives cost at scale and propose concrete strategies to reduce it.
- Add the following Cost Reasoning section below your failure modes table in url-shortener-design.md:
### Cost Reasoning
At 4,000 reads/second, the dominant costs are:
1. **Cache layer** (Redis): ~35GB in-memory cluster. This is the most expensive component per GB but provides sub-millisecond reads.
2. **Database read throughput**: Provisioned read capacity for the 20% of reads that miss cache.
3. **Bandwidth**: Minimal at 2 MB/s.
**To cut costs by 50%:**
- Increase cache TTL and cache size to push hit rate from 80% to 95% (fewer DB reads)
- Move URL mappings older than 90 days with zero recent reads to cold storage (S3)
- Use reserved capacity pricing instead of on-demand for the database
**Cost per URL created:** At this scale, approximately $0.000001 per URL (dominated by storage and amortized infrastructure).
What makes this cost section stand out?
Three things interviewers look for in cost reasoning. First, you identify the dominant cost drivers (not just listing everything). Second, you propose concrete reduction strategies with estimated impact ("80% to 95%" cache hit rate). Third, you provide a per-unit cost metric that shows you can think about the business, not just the system.
Moving cold data to S3 after 90 days is a standard cost optimization pattern. Most URLs are accessed heavily in their first few days and then rarely again. Cheap object storage handles the long tail.
- Save url-shortener-design.md.
- Read through your complete Section 7. You should see a trade-offs table (5 rows), a failure modes table (5 rows), and a cost reasoning paragraph with numbered cost drivers, reduction strategies, and a per-URL cost estimate.
Why cost reasoning is the 2026 differentiator
Before 2024, cost reasoning was a "bonus" in system design interviews. Now it is graded explicitly at the senior level. The shift reflects that companies want engineers who think about infrastructure as a budget item, not just a technical puzzle.
✔️ Awesome, I've got everything!
Great. Double check you have saved url-shortener-design.md with the complete Section 7.
ⓧ I'd like to double check the full code
Here is the complete Section 7 (Evaluation) as it should appear in your file:
## 7. Evaluation
### Trade-offs Made
| Decision | Chose | Over | Reason |
|----------|-------|------|--------|
| Database | NoSQL key-value | SQL/relational | Simple key lookup pattern, horizontal scaling at 3TB+ |
| Redirect code | 301 (permanent) | 302 (temporary) | Reduces server load, analytics solved separately |
| Key generation | Pre-generated pool | Hash/Counter | No collisions, O(1), random keys |
| Cache eviction | LRU | TTL-based | Keeps popular URLs hot; newly created URLs may get evicted before going viral, but viral URLs quickly re-enter cache |
| Partitioning | Consistent hashing | Range-based | Even distribution, no hot spots on popular prefixes |
### Failure Modes
| Component | Failure Impact | Recovery Strategy |
|-----------|---------------|-------------------|
| Load Balancer | All traffic drops | DNS failover to standby LB; health monitoring triggers automatic promotion |
| App Server (one) | Reduced capacity | LB health checks detect failure in seconds, route traffic to healthy servers |
| Cache (Redis) | Higher DB latency | Requests fall through to database. Not an outage, but P99 latency spikes. Redis Cluster provides replication |
| Database | Data unavailable | Read replicas promoted for reads. Write downtime until primary recovers or failover completes |
| Key Pool exhausted | Cannot create new URLs | Alert fires when pool drops below threshold. Background service replenishes. Existing redirects unaffected |
### Cost Reasoning
At 4,000 reads/second, the dominant costs are:
1. **Cache layer** (Redis): ~35GB in-memory cluster. This is the most expensive component per GB but provides sub-millisecond reads.
2. **Database read throughput**: Provisioned read capacity for the 20% of reads that miss cache.
3. **Bandwidth**: Minimal at 2 MB/s.
**To cut costs by 50%:**
- Increase cache TTL and cache size to push hit rate from 80% to 95% (fewer DB reads)
- Move URL mappings older than 90 days with zero recent reads to cold storage (S3)
- Use reserved capacity pricing instead of on-demand for the database
**Cost per URL created:** At this scale, approximately $0.000001 per URL (dominated by storage and amortized infrastructure).
Secret mission
Add Analytics Tracking
Your URL shortener works, but stakeholders want data. How many clicks is each short URL getting? Where are those clicks coming from? This is the exact follow-up question FAANG interviewers ask to test whether you can evolve a design under new constraints without breaking what already works.
Clean Up Your Resources
Clean Up Your Resources
This project runs entirely locally, so there are no ongoing costs. Your Excalidraw diagram and Markdown design document are both stored as local files on your machine.
Resources you used:
- url-shortener-design.md (local Markdown file).
- url-shortener-architecture.excalidraw (local Excalidraw diagram file).
Keep everything running
No action needed. Choose this if you plan to use these files for interview practice or future reference.
- Your design document and diagram stay on your machine, ready for review before interviews.
- You can reopen url-shortener-architecture.excalidraw at any time by visiting excalidraw.com and using File > Open.
- Consider exporting the Excalidraw diagram as PNG or SVG for easy sharing in presentations or portfolio sites.
Pause - I'll come back to this later
Since everything is local, there is nothing to pause. Your files remain on disk until you decide to delete them.
- Both files take up negligible disk space (a few kilobytes each).
- No background processes are running. Nothing consumes memory or CPU.
Delete - I don't want to use this again
Remove both project files from your machine entirely.
- Delete url-shortener-design.md and url-shortener-architecture.excalidraw from wherever you saved them (Desktop, Documents, or your project folder).
- On macOS or Linux, navigate to the folder containing your files and run:
rm url-shortener-design.md url-shortener-architecture.excalidraw
- On Windows, navigate to the folder containing your files and run:
del url-shortener-design.md, url-shortener-architecture.excalidraw
- No accounts were created and no browser data persists from Excalidraw. You are fully cleaned up.
That's a Wrap!
That's a Wrap!
Nice work. You just produced a complete system design document and architecture diagram for a URL shortener at the level FAANG interviewers expect for L4-L5 candidates.
You've learned how to:
- Produce a structured system design document following the RESHADED framework, including back-of-envelope capacity estimation with real numbers for QPS, storage, and bandwidth.
- Create a professional architecture diagram in Excalidraw with all major components, labeled data flow arrows, and annotations for both read and write paths.
- Articulate 5+ explicit trade-offs with alternatives and justification, analyze failure modes for every component, and reason about cost. These are the criteria that differentiate strong candidates in system design interviews.
- Secret Mission: Extended the design with analytics tracking, demonstrating the ability to evolve an architecture while managing new constraints like async processing, message queues, and OLAP vs OLTP separation.
Ready to quiz yourself?