The question “Design a URL shortener” is a classic system design interview problem. It appears simple on the surface but allows interviewers to evaluate a wide range of skills:
- Requirements gathering
- API design
- Database modeling
- Hashing and encoding techniques
- Scalability
- Caching
- Load balancing
- Availability and fault tolerance
- Performance optimization
In this article, we’ll walk through a complete system design interview answer for building a URL shortening service such as TinyURL, Bitly.
By the end of this guide, you’ll know how to structure your response, what tradeoffs to discuss, and how to present a production-ready architecture.
What Is a URL Shortener?
A URL shortener is a service that converts a long URL into a shorter, more manageable link.
For example:
Original URL:
https://www.example.com/products/category/electronics/laptops/gaming-laptop-15-inch-rtx-4070?ref=homepage&campaign=summer_sale
Shortened URL:
https://short.ly/aB3xY9
When a user clicks the shortened link, the system redirects them to the original long URL.
Why Is This a Popular System Design Interview Question?
Interviewers like this question because it tests both basic system design concepts and advanced scalability topics.
It allows discussion around:
- Read-heavy vs write-heavy systems
- Key-value storage
- Database indexing
- Distributed ID generation
- Caching strategies
- Redirect mechanisms
- Rate limiting
- Analytics
- High availability
- Consistency vs availability tradeoffs
A strong answer should not jump directly into implementation. Instead, it should begin with requirements clarification.
Step 1: Clarify Requirements
In any system design interview, always start by clarifying the scope.
You can ask questions like:
- What features should the URL shortener support?
- Is the system read-heavy or write-heavy?
- How many requests per second should we support?
- Do we need analytics?
- Do shortened URLs expire?
- Do users need accounts?
- Do we need custom short links?
- Should we prevent malicious links?
- What is the expected availability?
- What latency is acceptable?
For this article, we’ll define a reasonable interview scope.
Functional Requirements
The system should support the following core features:
1. Shorten a URL
Given a long URL, the system should return a shortened URL.
Example:
POST /shorten
{
"long_url": "https://www.example.com/some/long/path"
}
Response:
{
"short_url": "https://short.ly/aB3xY9"
}
2. Redirect a Short URL
Given a short URL, the system should redirect the user to the original long URL.
Example:
GET /aB3xY9
Response:
HTTP/1.1 301 Moved Permanently
Location: https://www.example.com/some/long/path
Or:
HTTP/1.1 302 Found
Location: https://www.example.com/some/long/path
3. Custom Short Links
Users may be allowed to create custom aliases.
Example:
https://short.ly/my-product-launch
4. Link Expiration
Some short links may expire after a certain time.
Example:
expires_at = 2026-12-31T23:59:59Z
5. Basic Analytics
The system may track:
- Number of clicks
- Referrer
- User agent
- Country
- Device type
- Timestamp
6. User Accounts
Optional but common:
- Users can view their created links
- Users can delete links
- Users can manage analytics
- Users can set expiration dates
Non-Functional Requirements
Non-functional requirements are often more important than functional requirements in system design interviews.
1. Low Latency
Redirects should be extremely fast.
Target:
Redirect latency < 100 ms
Ideally:
Redirect latency < 50 ms
2. High Availability
URL shorteners are often used in public links, marketing campaigns, and social media. Downtime is unacceptable.
Target:
99.99% availability
3. Scalability
The system should handle a large number of read requests.
URL shortening systems are typically read-heavy.
For example:
- Many users click shortened links.
- Fewer users create shortened links.
A typical read/write ratio could be:
90% reads
10% writes
Or even:
99% reads
1% writes
4. Reliability
Shortened URLs should not disappear.
If a short link is created, it should reliably resolve to the original URL unless intentionally deleted or expired.
5. Consistency
For most URL shorteners, strong consistency is not always necessary for analytics, but the mapping between short code and long URL should be reliable.
We can tolerate eventual consistency for analytics but not for core redirects.
6. Security
The system should protect against:
- Malicious URLs
- Spam
- Brute-force enumeration
- Abuse by bots
- Phishing links
- SQL injection
- XSS if links are displayed in a dashboard
Step 2: Estimate Capacity
Capacity estimation helps demonstrate engineering maturity.
Let’s make reasonable assumptions.
Assumptions
Suppose our system receives:
100 million new URLs per month
And:
10 billion redirects per month
This gives a read/write ratio of:
10,000,000,000 / 100,000,000 = 100:1
So the system is heavily read-dominant.
Writes Per Second
100 million writes per month
Approximate seconds per month:
30 days × 24 hours × 3600 seconds = 2,592,000 seconds
Writes per second:
100,000,000 / 2,592,000 ≈ 38.6 writes/sec
We can round this to:
~40 writes/sec
Reads Per Second
10 billion reads per month
Reads per second:
10,000,000,000 / 2,592,000 ≈ 3,858 reads/sec
We can round this to:
~4,000 reads/sec
Peak Traffic
Systems often need to handle 2x to 5x average traffic.
If we assume 5x peak:
Writes: 40 × 5 = 200 writes/sec
Reads: 4,000 × 5 = 20,000 reads/sec
So our system should be designed to handle:
20,000 reads/sec
200 writes/sec
Storage Estimation
Assume each URL record contains:
| Field | Size |
|---|---|
| short_code | 10 bytes |
| long_url | 500 bytes |
| created_at | 8 bytes |
| expires_at | 8 bytes |
| user_id | 8 bytes |
| metadata | 100 bytes |
Approximate record size:
~650 bytes
If we store 100 million URLs per month:
100,000,000 × 650 bytes = 65 GB per month
Per year:
65 GB × 12 = 780 GB/year
For five years:
780 GB × 5 = 3.9 TB
So storage is significant but manageable with distributed storage and archival policies.
If we store analytics events, storage grows much faster.
For example, if each redirect produces an analytics event:
10 billion events/month
If each event is 200 bytes:
10,000,000,000 × 200 bytes = 2 TB/month
Analytics data can quickly become larger than the URL mapping data.
Therefore, analytics should usually be stored separately from the core URL mapping database.
Step 3: Define the Core Data Model
The most important data structure is a mapping:
short_code -> long_url
Example:
| short_code | long_url |
|---|---|
| aB3xY9 | https://www.example.com/very/long/url |
The short code must be:
- Unique
- URL-safe
- Short
- Difficult to guess
- Efficient to index
How Long Should the Short Code Be?
Short codes are usually generated using characters from:
a-z
A-Z
0-9
This gives:
26 lowercase + 26 uppercase + 10 digits = 62 characters
This is called Base62 encoding.
Number of possible codes for length n:
62^n
For length 6:
62^6 = 56,800,235,584
That is approximately:
56.8 billion combinations
For many systems, a 6-character short code is enough.
For larger scale, use 7 characters:
62^7 = 3,521,614,606,208
That is over 3.5 trillion combinations.
A good default choice:
6 to 7 characters
Step 4: Choose an ID Generation Strategy
The core challenge is generating a unique short code for every long URL.
There are several possible strategies.
Option 1: Hash the Long URL
One simple idea is to hash the long URL using MD5, SHA-1, or SHA-256, then encode the result.
Example:
SHA-256(long_url) -> Base62 -> first 7 characters
Advantages
- Simple
- Same long URL produces the same short code
- Reduces duplicate entries
Disadvantages
- Hash collisions are possible
- Need collision detection and resolution
- Predictable if the same URL always maps to the same short code
- May leak information about repeated URLs
Collision handling is required.
If two different long URLs generate the same short code, the system must append extra data and hash again, or use another resolution mechanism.
Option 2: Random Short Code Generation
Generate a random 6 or 7 character Base62 string.
Example:
aB3xY9
Then check if it already exists in the database.
If it exists, generate another one.
Advantages
- Simple to understand
- Harder to enumerate than sequential IDs
- No direct relationship between URLs
Disadvantages
- Database lookup required for every generation
- Collision probability increases as codes are used
- May become slow under heavy write load unless optimized
This is acceptable for small systems but not ideal for massive scale.
Option 3: Distributed Unique ID Generator
A more scalable approach is to use a centralized or distributed unique ID generator.
The system generates a unique numeric ID, then converts it to Base62.
Example:
ID = 123456789
Base62(ID) = aB3xY9
This is one of the most common production-ready approaches.
Possible implementations:
- Database auto-increment ID
- Redis INCR
- Snowflake-like ID generator
- ZooKeeper sequence generator
- Dedicated ID generation service
- Key-value store counter
- Database sequence
Why Base62 Encoding Works Well
Suppose we generate a unique numeric ID:
125000000
We can convert it to Base62:
Base62(125000000) -> short_code
This gives us:
- Compact representation
- URL-safe characters
- No special characters
- Efficient lookup
- Deterministic conversion
Base62 avoids characters that may cause issues in URLs, such as:
/
+
=
?
&
Unlike Base64, Base62 is safe for direct use in URLs.
Handling Collisions
Even with unique ID generation, collisions should be considered.
With a unique numeric ID converted to Base62, collisions should not occur if the ID generator is truly unique.
However, with hashing or random generation, collisions must be handled.
Possible strategies:
- Check if short code exists.
- If it exists, generate a new code.
- Append random suffix and retry.
- Use database unique constraint.
- Use conditional write.
Example database constraint:
UNIQUE(short_code)
If a collision occurs, retry with a new short code.
Step 5: Design the API
A clean API design is essential.
We’ll use REST-style APIs for simplicity.
Create Short URL
Request
POST /v1/shorten
Body:
{
"long_url": "https://www.example.com/products/laptop?ref=homepage",
"custom_code": "summer-sale",
"expires_at": "2026-12-31T23:59:59Z"
}
Response
{
"short_code": "summer-sale",
"short_url": "https://short.ly/summer-sale",
"long_url": "https://www.example.com/products/laptop?ref=homepage",
"created_at": "2026-06-01T12:00:00Z",
"expires_at": "2026-12-31T23:59:59Z"
}
Redirect Short URL
Request
GET /{short_code}
Example:
GET /aB3xY9
Response
HTTP/1.1 302 Found
Location: https://www.example.com/products/laptop?ref=homepage
Get Link Metadata
Request
GET /v1/links/{short_code}
Response
{
"short_code": "aB3xY9",
"long_url": "https://www.example.com/products/laptop?ref=homepage",
"created_at": "2026-06-01T12:00:00Z",
"expires_at": "2026-12-31T23:59:59Z",
"click_count": 15423
}
Delete Link
Request
DELETE /v1/links/{short_code}
Response
{
"message": "Link deleted successfully"
}
Get Analytics
Request
GET /v1/links/{short_code}/analytics
Response
{
"short_code": "aB3xY9",
"total_clicks": 15423,
"clicks_last_24_hours": 532,
"top_countries": [
{
"country": "US",
"clicks": 7200
},
{
"country": "IN",
"clicks": 3100
}
]
}
Step 6: Choose the Right Database
The primary query pattern is:
SELECT long_url
FROM urls
WHERE short_code = ?
This is a simple key-value lookup.
The database should support:
- Low-latency reads
- Unique constraints
- Horizontal scaling
- High availability
- Efficient indexing
Possible options:
| Database Type | Examples | Good Fit? |
|---|---|---|
| Relational DB | PostgreSQL, MySQL | Yes, for structured data |
| NoSQL Key-Value | DynamoDB, Redis, Cassandra | Excellent for scale |
| Document DB | MongoDB | Possible but not ideal |
| Object Storage | S3 | Not ideal for primary lookup |
Recommended Primary Storage
For a large-scale URL shortener, a distributed key-value store or wide-column store is often ideal.
Examples:
- Amazon DynamoDB
- Apache Cassandra
- ScyllaDB
- Redis with persistence
- HBase
- Google Cloud Bigtable
For smaller systems, PostgreSQL or MySQL can work very well.
Example SQL Schema
CREATE TABLE urls (
id BIGINT PRIMARY KEY,
short_code VARCHAR(16) NOT NULL UNIQUE,
long_url TEXT NOT NULL,
user_id BIGINT,
created_at TIMESTAMP NOT NULL,
expires_at TIMESTAMP,
is_deleted BOOLEAN DEFAULT FALSE
);
CREATE INDEX idx_short_code ON urls(short_code);
If using PostgreSQL:
CREATE INDEX idx_urls_short_code ON urls(short_code);
If custom codes are common, also consider:
CREATE INDEX idx_user_id ON urls(user_id);
Example NoSQL Design
For DynamoDB or Cassandra:
URL Table
Partition key:
short_code
Attributes:
short_code
long_url
created_at
expires_at
user_id
is_deleted
Example item:
{
"short_code": "aB3xY9",
"long_url": "https://www.example.com/products/laptop",
"created_at": "2026-06-01T12:00:00Z",
"expires_at": null,
"user_id": "987654",
"is_deleted": false
}
This gives O(1) lookup by short code.
Step 7: High-Level Architecture
A basic high-level architecture looks like this:
Client
↓
Load Balancer
↓
API Gateway / Application Servers
↓
URL Service
↓
Primary Database
For reads:
Client → Load Balancer → Redirect Service → Cache → Database
For writes:
Client → Load Balancer → URL Creation Service → ID Generator → Database
Detailed Architecture Components
1. Client
The client can be:
- Web browser
- Mobile app
- API consumer
- Admin dashboard
2. Load Balancer
Distributes incoming traffic across application servers.
Examples:
- NGINX
- HAProxy
- AWS ALB
- Google Cloud Load Balancer
- Azure Load Balancer
3. API Gateway
Handles:
- Authentication
- Rate limiting
- Request validation
- Routing
- Throttling
- Logging
- API versioning
4. URL Shortening Service
Handles:
- Creating shortened URLs
- Validating input
- Generating short codes
- Saving mappings
5. Redirect Service
Handles:
- Looking up short codes
- Redirecting users
- Checking expiration
- Emitting analytics events
In early designs, this can be the same service as the URL shortening service.
At scale, separate services are better.
6. Cache
Stores frequently accessed short-code-to-long-URL mappings.
Examples:
- Redis
- Memcached
- CDN edge cache
7. Primary Database
Stores durable URL mappings.
8. Message Queue
Handles asynchronous analytics processing.
Examples:
- Apache Kafka
- AWS Kinesis
- RabbitMQ
- Google Pub/Sub
9. Analytics Pipeline
Processes click events.
Components:
- Stream processor
- Data warehouse
- Aggregation service
- Dashboard
Step 8: Read Flow
When a user clicks a shortened URL:
https://short.ly/aB3xY9
The system performs the following steps:
- Request reaches load balancer.
- Load balancer routes request to redirect service.
- Redirect service checks cache.
- If cache hit, return long URL.
- If cache miss, query database.
- Store result in cache.
- Return redirect response.
- Emit click event asynchronously.
Example:
GET /aB3xY9
Cache lookup:
short_code = aB3xY9
Cache hit:
long_url = https://www.example.com/products/laptop
Response:
HTTP/1.1 302 Found
Location: https://www.example.com/products/laptop
Step 9: Write Flow
When a user creates a shortened URL:
- Client sends long URL to API.
- API validates URL.
- System checks rate limits.
- ID generator creates unique ID.
- Unique ID is encoded into Base62.
- Mapping is stored in database.
- Short URL is returned to client.
Example:
POST /v1/shorten
Body:
{
"long_url": "https://www.example.com/products/laptop"
}
ID generator:
ID = 9876543210
Base62:
short_code = aB3xY9
Store:
aB3xY9 -> https://www.example.com/products/laptop
Return:
{
"short_url": "https://short.ly/aB3xY9"
}
Step 10: Caching Strategy
Since the system is read-heavy, caching is essential.
A large percentage of clicks usually go to a small percentage of popular links.
This is a classic case where the Pareto principle applies:
80% of clicks may come from 20% of links
Or even:
95% of clicks may come from 1% of links
Therefore, caching popular short codes dramatically reduces database load.
What Should Be Cached?
Cache the mapping:
short_code -> long_url
Example:
Key: aB3xY9
Value: https://www.example.com/products/laptop
TTL: 24 hours
You may also cache:
short_code -> metadata
But for redirect performance, only the long URL is needed.
Cache Eviction Policies
Possible eviction policies:
- LRU: Least Recently Used
- LFU: Least Frequently Used
- TTL-based expiration
For URL shorteners, TTL plus LRU is often effective.
Example:
TTL = 1 hour
Max memory = 16 GB
Eviction = allkeys-lru
Cache Invalidation
Cache invalidation is needed when:
- A link is deleted
- A link expires
- A link is updated
- A link is disabled for abuse
For simplicity, use TTL expiration.
For stronger correctness, invalidate explicitly:
DELETE cache_key
Example:
DEL aB3xY9
If link updates are rare, TTL-only invalidation may be sufficient.
Handling Cache Misses
If a short code is not in cache:
- Query database.
- If found, write to cache.
- Return redirect.
- If not found, return 404.
To protect the database from cache stampedes, use:
- Request coalescing
- Mutex locks
- Negative caching
- Rate limiting
Example negative cache:
Key: aB3xY9
Value: NOT_FOUND
TTL: 60 seconds
Step 11: Use a CDN for Extra Performance
A CDN can improve redirect performance by caching responses closer to users.
However, URL redirects are often personalized or analytics-sensitive, so caching at the CDN requires care.
A CDN can help with:
- Static assets
- Public landing pages
- Rate-limited redirect responses
- Edge caching for popular links
For redirects, edge logic can:
- Check cache
- Forward misses to origin
- Add analytics headers
- Enforce geo-based routing
- Block malicious traffic
Examples:
- CloudFront Functions
- Cloudflare Workers
- Fastly Compute
- Akamai EdgeWorkers
Step 12: Redirect Status Codes
An important interview discussion is whether to use:
301 Moved Permanently
or:
302 Found
301 Redirect
Meaning:
The resource has permanently moved.
Browsers and CDNs may cache the redirect.
Advantages:
- Faster future redirects
- Less server load
Disadvantages:
- Analytics may be lost because browser does not hit the shortener again
- Harder to change destination later
302 Redirect
Meaning:
The resource has temporarily moved.
Browsers usually do not cache the redirect aggressively.
Advantages:
- Better analytics
- Destination can change
- More control
Disadvantages:
- More traffic to redirect service
- Higher load
For URL shorteners with analytics, 302 is often preferred.
For pure performance without analytics, 301 may be acceptable.
A strong interview answer:
I would use 302 redirects by default because analytics are important. If caching and lower load become more important than click tracking, we can selectively use 301 for certain links or cache popular redirects at the edge.
Step 13: Scalability Considerations
The system must handle high read traffic.
Key scaling techniques:
- Horizontal scaling of application servers
- Load balancing
- Distributed cache
- Database replication
- Database sharding
- Read replicas
- Message queues for analytics
- CDN caching
- Rate limiting
- Async processing
Scaling the Application Tier
Use stateless application servers.
Stateless design allows:
- Easy horizontal scaling
- Simple load balancing
- Faster failover
- Deployment without session affinity
Example:
NGINX Load Balancer
├── Redirect Service Instance 1
├── Redirect Service Instance 2
├── Redirect Service Instance 3
└── Redirect Service Instance N
Scaling the Database
For small to medium scale, use:
Primary DB + Read Replicas
Writes go to primary.
Reads can go to replicas.
Example:
Write: Primary PostgreSQL
Read: PostgreSQL Read Replica
For larger scale, use sharding.
Database Sharding
Sharding divides data across multiple database nodes.
Possible shard keys:
- short_code
- user_id
- created_at
- hashed short_code
For redirect lookups, sharding by short_code is usually best.
Example:
shard_id = hash(short_code) % number_of_shards
This allows the system to locate the correct shard quickly.
Consistent Hashing
If using distributed storage, consistent hashing can help distribute keys evenly.
Benefits:
- Balanced load
- Easier node addition/removal
- Reduced reshuffling
Examples:
- Cassandra
- DynamoDB
- Riak
- Memcached clients
- Redis Cluster
Step 14: High Availability and Fault Tolerance
A URL shortener should remain available even if some components fail.
Techniques:
- Multi-zone deployment
- Multi-region replication
- Database replicas
- Cache replicas
- Health checks
- Automatic failover
- Load balancer retries
- Graceful degradation
Handling Database Failure
If the primary database fails:
- Promote a replica
- Route writes to new primary
- Continue serving reads from cache and replicas
- Queue writes if necessary
If cache fails:
- Fall back to database
- Use request throttling
- Use negative caching
- Rebuild cache gradually
Step 15: Analytics Design
Analytics are often a major part of real-world URL shorteners.
Each click can generate an event:
{
"short_code": "aB3xY9",
"timestamp": "2026-06-01T12:00:00Z",
"ip_hash": "hashed_ip",
"user_agent": "Mozilla/5.0...",
"referrer": "https://twitter.com",
"country": "US",
"device_type": "mobile"
}
Do not block the redirect on analytics processing.
Instead:
Redirect first, analytics later
Use a message queue:
Redirect Service → Kafka → Analytics Pipeline
This keeps redirect latency low.
Analytics Architecture
A scalable analytics pipeline:
Redirect Service
↓
Kafka/Kinesis
↓
Stream Processor
↓
Aggregated Storage
↓
Analytics API/Dashboard
Possible technologies:
| Component | Options |
|---|---|
| Message Queue | Kafka, Kinesis, RabbitMQ |
| Stream Processing | Flink, Spark Streaming, Kafka Streams |
| Storage | ClickHouse, BigQuery, Snowflake, Druid |
| Real-Time Metrics | Redis, DynamoDB |
| Dashboards | Grafana, Superset, custom UI |
Real-Time Click Counter
For basic click counts, use Redis:
INCR clicks:aB3xY9
Example:
Key: clicks:aB3xY9
Value: 15423
For time-based analytics:
INCR clicks:aB3xY9:2026-06-01:12
This allows clicks per hour.
Step 16: Security and Abuse Prevention
URL shorteners can be abused for:
- Phishing
- Malware distribution
- Spam
- Ad fraud
- Bot traffic
- Brute-force scanning
Security measures are important.
1. URL Validation
Validate the long URL:
- Must be a valid URL
- Must use allowed schemes, usually
httporhttps - Reject suspicious domains
- Check maximum length
Example:
Allowed schemes: http, https
Max URL length: 2048 characters
2. Malicious Link Detection
Integrate with:
- Safe Browsing APIs
- Virus scanning services
- Domain reputation systems
- ML-based fraud detection
If a link is malicious:
- Block creation
- Disable redirect
- Show warning page
- Report abuse
3. Rate Limiting
Prevent abuse by limiting API calls.
Examples:
Anonymous user: 10 links/minute
Authenticated user: 100 links/minute
IP-based redirect limit: 1000 requests/minute
Use:
- Token bucket
- Sliding window
- Redis-based counters
4. Authentication and Authorization
For user-owned links:
- Require API keys or OAuth
- Verify ownership before deletion/update
- Scope permissions
Example:
Authorization: Bearer <token>
5. Prevent Enumeration
Sequential IDs are easy to enumerate.
Example:
/aB3xY0
/aB3xY1
/aB3xY2
To reduce enumeration risk:
- Use random-looking Base62 codes
- Avoid exposing internal IDs
- Add rate limiting
- Use longer codes if needed
However, if codes are generated from sequential IDs and Base62 encoded, they may still be somewhat predictable. If unpredictability is important, add randomization or encryption.
Step 17: Custom Short Links
Custom links improve branding.
Example:
https://short.ly/summer-sale
Challenges:
- Must be unique
- May contain reserved words
- May be abusive
- May require validation
Implementation:
- User requests custom code.
- System validates format.
- System checks availability.
- If available, reserve atomically.
- Store mapping.
Use database unique constraint:
UNIQUE(short_code)
Or conditional write in DynamoDB:
attribute_not_exists(short_code)
Reserved words should be blocked:
admin
api
login
dashboard
settings
help
support
Step 18: Link Expiration
Some links should expire.
Example:
expires_at = 2026-12-31T23:59:59Z
On redirect:
if current_time > expires_at:
return 404 or 410
To avoid scanning the database constantly, use:
- TTL in cache
- Background cleanup jobs
- Time-to-live indexes
- Lazy expiration during read
- Scheduled deletion tasks
Example lazy expiration:
Read short_code
If expired:
return 404
mark as expired
Step 19: Multi-Region Design
For global scale, deploy across regions.
Example:
US-East
EU-West
AP-South
Each region can have:
- Application servers
- Cache
- Read replicas
- Message queue
- CDN edge
Writes can be:
- Single-region primary
- Multi-region active-active
- Region-local writes with async replication
For simplicity, many systems use:
Single write region
Multiple read regions
For higher availability:
Active-active multi-region
But this introduces conflict resolution complexity.
Step 20: Consistency Tradeoffs
The URL mapping must be reliable.
If a short code is created, users should be able to resolve it quickly.
This suggests strong consistency for writes or at least read-after-write consistency.
For analytics, eventual consistency is acceptable.
A good tradeoff:
| Data Type | Consistency Model |
|---|---|
| short_code to long_url | Strong or read-after-write |
| click analytics | Eventual |
| dashboard metrics | Eventual |
| abuse flags | Near real-time |
| custom link reservation | Strong |
Step 21: Monitoring and Observability
A production system needs monitoring.
Track:
- Redirect latency
- Cache hit ratio
- Database query latency
- Error rate
- 404 rate
- 5xx rate
- QPS
- CPU/memory usage
- Queue lag
- Analytics pipeline delay
- Rate-limit rejections
Example metrics:
p50 redirect latency
p95 redirect latency
p99 redirect latency
cache_hit_rate
database_read_latency
kafka_consumer_lag
Use:
- Prometheus
- Grafana
- Datadog
- New Relic
- CloudWatch
- OpenTelemetry
Step 22: Possible Failure Scenarios and Mitigations
Scenario 1: Cache Failure
Impact:
Increased database load
Mitigation:
- Cache replicas
- Fallback to database
- Rate limiting
- Request coalescing
Scenario 2: Database Overload
Impact:
Slow redirects
Timeouts
Mitigation:
- Add read replicas
- Increase cache TTL
- Serve stale cache temporarily
- Shard database
- Throttle non-critical traffic
Scenario 3: ID Generator Failure
Impact:
Cannot create new links
Mitigation:
- Highly available ID generator
- Preallocated ID ranges
- Fallback generator
- Retry with backoff
Scenario 4: Analytics Pipeline Failure
Impact:
Analytics delayed or lost
Mitigation:
- Durable queue
- Replay events
- Dead-letter queue
- Monitoring lag
Redirects should continue to work even if analytics fails.
Step 23: Optimizations for Low Latency
To make redirects extremely fast:
- Cache hot links.
- Use CDN edge caching.
- Keep redirect service stateless.
- Use connection pooling.
- Use asynchronous analytics.
- Avoid heavy processing in redirect path.
- Use efficient serialization.
- Use HTTP/2 or HTTP/3.
- Deploy close to users.
- Index short_code efficiently.
The redirect path should be as lightweight as possible.
Step 24: Example System Design Answer Structure
In an interview, structure your answer like this:
1. Clarify Requirements
I’d like to start by clarifying the scope. Are we building a basic TinyURL-like service with redirect and analytics? Is it read-heavy? Do we need custom links, user accounts, and expiration?
2. Define Functional Requirements
Core features are shortening a URL, redirecting to the original URL, optional custom aliases, expiration, and analytics.
3. Define Non-Functional Requirements
The system should be highly available, low latency, scalable, and reliable. It is likely read-heavy.
4. Estimate Scale
If we assume 100 million links created per month and 10 billion redirects per month, we need around 4,000 reads/sec and 40 writes/sec on average, with peak around 20,000 reads/sec.
5. Data Model
The core table maps short_code to long_url. The short_code should be unique and indexed.
6. ID Generation
I would use a distributed unique ID generator and convert IDs to Base62. This avoids collisions and scales well.
7. API Design
We need a POST endpoint to create links and a GET endpoint to redirect.
8. High-Level Architecture
Clients connect through a load balancer to stateless API servers. URL mappings are stored in a scalable database. A cache handles hot reads.
9. Caching
Use Redis or CDN caching for popular links. Cache the short_code to long_url mapping.
10. Analytics
Emit click events asynchronously to Kafka and process them separately so redirects remain fast.
11. Tradeoffs
Use 302 redirects for analytics, strong consistency for link creation, eventual consistency for analytics.
Simplified Architecture Diagram
┌────────────┐
│ Client │
└─────┬──────┘
│
┌─────▼──────┐
│ CDN │
└─────┬──────┘
│
┌─────▼──────┐
│ Load │
│ Balancer │
└─────┬──────┘
│
┌───────────────┼───────────────┐
│ │ │
┌──────▼─────┐ ┌──────▼─────┐ ┌──────▼─────┐
│ Redirect │ │ Redirect │ │ URL Create │
│ Service │ │ Service │ │ Service │
└──────┬─────┘ └──────┬─────┘ └──────┬─────┘
│ │ │
│ │ ┌──────▼─────┐
│ │ │ ID │
│ │ │ Generator │
│ │ └──────┬─────┘
│ │ │
┌──────▼───────────────▼───────────────▼─────┐
│ Cache │
│ Redis/Memcached │
└──────────────────┬─────────────────────────┘
│
┌───────▼────────┐
│ URL Database │
└───────┬────────┘
│
┌───────▼────────┐
│ Message Queue │
│ Kafka/Kinesis │
└───────┬────────┘
│
┌───────▼────────┐
│ Analytics │
│ Pipeline │
└────────────────┘
Sample Database Choices by Scale
Small Scale
Use:
PostgreSQL
Redis Cache
NGINX
Architecture:
App Server → PostgreSQL
App Server → Redis
Good for:
- Startup product
- Internal tool
- Low traffic service
Medium Scale
Use:
PostgreSQL with read replicas
Redis Cluster
Load Balancer
Kafka
Architecture:
Stateless App Servers
Redis Cache
Primary DB + Read Replicas
Kafka for analytics
Good for:
- Public SaaS
- Moderate viral traffic
- Marketing links
Large Scale
Use:
DynamoDB/Cassandra
Redis Cluster
CDN
Kafka/Kinesis
Multi-region deployment
Architecture:
Edge/CDN
Load Balancers
Stateless Redirect Services
Distributed Cache
Distributed Key-Value Store
Async Analytics Pipeline
Good for:
- Global URL shortener
- Billions of clicks
- High availability requirements
Common Interview Follow-Up Questions
1. What happens if two users shorten the same long URL?
Possible approaches:
- Return the same short code
- Create different short codes
- Let users choose custom codes
If deduplicating:
Hash(long_url) -> short_code
But collision handling is still required.
If not deduplicating:
Each request gets a unique ID
This is simpler and often preferred.
2. How do you prevent malicious URLs?
Use:
- URL validation
- Domain reputation
- Safe Browsing APIs
- Rate limiting
- Abuse reporting
- ML classification
- Manual review
3. How do you support billions of links?
Use:
- Distributed ID generation
- Sharded database
- Caching
- CDN
- Stateless services
- Async analytics
4. How do you make redirects faster?
Use:
- Cache
- CDN
- Read replicas
- Edge functions
- Minimal processing
- Connection reuse
5. How do you handle expired links?
Check expiration during redirect.
Use:
if expires_at < current_time:
return 404 or 410
Cleanup can happen asynchronously.
6. How do you handle hot keys?
A hot key is a very popular short link receiving massive traffic.
Mitigations:
- Cache hot key
- Replicate hot key across cache nodes
- Use CDN caching
- Rate limit abusive clients
- Serve from edge
7. What if the cache becomes inconsistent?
Use:
- TTL expiration
- Explicit invalidation
- Versioned records
- Write-through or write-around cache patterns
For critical updates, invalidate cache immediately.
Best Practices for a Strong Interview Answer
- Start with requirements.
- Quantify scale.
- Identify read/write ratio.
- Keep the core design simple.
- Discuss tradeoffs.
- Separate analytics from redirect path.
- Emphasize caching.
- Mention security and abuse prevention.
- Use diagrams if possible.
- Explain why you choose each component.
Avoid jumping straight into code or database tables.
Interviewers want to see structured thinking.
Example Final Architecture Summary
A strong final design could be:
Client
↓
CDN / Edge
↓
Load Balancer
↓
Stateless Redirect Service
↓
Redis Cache
↓
Distributed Key-Value Store
For link creation:
Client
↓
API Gateway
↓
URL Creation Service
↓
Distributed ID Generator
↓
Distributed Key-Value Store
For analytics:
Redirect Service
↓
Kafka
↓
Stream Processor
↓
Analytics Database
Key design decisions:
- Use Base62 short codes
- Use unique ID generator
- Use 302 redirects
- Cache hot links
- Process analytics asynchronously
- Shard by short_code
- Rate limit creation and redirect APIs
- Store analytics separately from URL mappings
Conclusion
Designing a URL shortener is an excellent system design interview question because it starts simple but quickly expands into important distributed systems topics.
A successful answer should cover:
- Functional and non-functional requirements
- Capacity estimation
- API design
- Data modeling
- ID generation
- Caching
- Redirect behavior
- Scalability
- High availability
- Analytics
- Security
The key insight is that a URL shortener is a read-heavy, low-latency key-value lookup system. The most important design decisions revolve around fast redirects, efficient key generation, caching, and asynchronous analytics processing.
If you can clearly explain these tradeoffs and present a clean architecture, you will demonstrate strong system design skills.
Frequently Asked Questions
1. Is a URL shortener read-heavy or write-heavy?
A URL shortener is usually read-heavy. Many users click shortened links, while relatively fewer users create new links. The read/write ratio can be 100:1 or higher.
2. What database is best for a URL shortener?
For small systems, PostgreSQL or MySQL is sufficient. For large-scale systems, a distributed key-value store such as DynamoDB, Cassandra, or Bigtable is often better.
3. Why use Base62 instead of Base64?
Base62 uses URL-safe characters only:
a-z
A-Z
0-9
Base64 may include characters like /, +, and =, which can require URL encoding.
4. Should I use 301 or 302 redirects?
Use 302 if analytics are important because browsers may not hit your server again after a cached 301 redirect. Use 301 if performance and caching are more important than analytics.
5. How do you generate unique short codes?
A common approach is to generate a unique numeric ID using a distributed ID generator and convert it to Base62. This avoids collisions and scales well.
6. How do you scale a URL shortener?
Use stateless services, load balancing, caching, database replication, sharding, CDN caching, and asynchronous analytics processing.
7. How do you handle analytics without slowing down redirects?
Send click events asynchronously to a message queue such as Kafka or Kinesis. Process analytics separately from the redirect path.
8. How do you prevent abuse?
Use rate limiting, URL validation, malicious link scanning, authentication, domain reputation checks, and abuse reporting mechanisms.