The URL shortener design is more than just a classic interview question; it’s a fascinating microcosm of modern web architecture. From Bitly to TinyURL, these services are a daily part of our online lives, seamlessly redirecting billions of clicks. But have you ever wondered what powers these deceptively simple tools? How do they handle millions of users and generate unique, clean links in the blink of an eye?
This isn’t just about making a long link shorter. It’s about designing a system that is fast, reliable, and massively scalable. Whether you’re a budding software engineer preparing for an interview or a seasoned developer looking to sharpen your architectural skills, this guide is for you. We’ll walk you through a persuasive, step-by-step tutorial, transforming a basic concept into a robust, production-ready system. Let’s design something amazing together.
Step 1: The Blueprint – Nailing Down Requirements
Before writing a single line of code, the foundation of any great URL shortener design is a crystal-clear understanding of its requirements. Getting this step right prevents costly redesigns later. We can break this down into two categories: what the system does (Functional) and how it behaves (Non-Functional).
Functional Requirements
These are the core features the user interacts with:
- Generate Short URL: A user provides a long URL, and the system returns a unique, much shorter URL.
- Redirection: When someone accesses the short URL, they must be redirected to the original long URL.
- Custom Short Links: Users should have the option to choose their own custom alias (e.g.,
our.domain/my-event) for better branding and readability. - Update and Delete: The creator of a link should be able to update its destination or delete it entirely.
- Expiry Time: Links can be set to expire after a certain period (e.g., 24 hours, one month, or a year), after which they will no longer work.
Non-Functional Requirements
These define the system’s quality and performance, especially under load:
- High Availability: The system must be highly available. Downtime means broken links, which can be catastrophic for users who rely on them. We should aim for 99.99% uptime.
- Low Latency: Redirection needs to be lightning-fast. Users expect to land on the destination page almost instantly. A delay of more than a few hundred milliseconds is noticeable and unacceptable.
- Scalability: The system must handle a massive scale. Let’s imagine a target of 100 million daily active users and 200 million new URLs created per month. This means our design must be horizontally scalable.
Step 2: The Heart of the System – A Deep Dive into Generation Logic
Now for the most interesting part of the URL shortener design: how do we generate the short code? This single decision has huge implications for uniqueness, predictability, and scalability.
Approach A: The Simple (But Flawed) Auto-Increment
The most straightforward idea is to use an auto-incrementing number from a database.
- Link 1 gets ID
1. - Link 2 gets ID
2. - …
- Link 1,000,000 gets ID
1000000.
The problem? These IDs aren’t very short. We can fix that by converting the ID into a different base. Instead of Base-10 (0-9), we can use Base62 (0-9, a-z, A-Z).
- ID
1->b - ID
1000->gE - ID
1,000,000->qX0
This is much shorter! However, a critical flaw remains: what happens when a user wants to create a custom URL like our.domain/gE? It would clash with the auto-generated link for ID 1000. This conflict makes the simple auto-increment approach difficult to manage at scale.
Approach B: The Robust Hashing & Encoding Method (The Right Way)
A far better approach is to generate a unique, non-conflicting key. The best practice is to use a globally unique 64-bit number (like a sequence from a dedicated generator like Twitter’s Snowflake) and then encode it to Base62 or Base58.
Why Base62/Base58?
You might be familiar with Base64, but it often includes characters like + and /, which are not URL-safe and require special encoding. Base62 (A-Z, a-z, 0-9) and Base58 (Base62 minus 0, O, I, l to avoid confusion) are perfectly URL-safe and human-readable. You can learn more about these encoding schemes from resources on character encoding for URLs.
This method gives us a short, unique, and non-predictable string that won’t clash with custom URLs. This is the core of a scalable URL shortener design.
Step 3: Building the Skeleton – API and Database Schema
With our logic defined, let’s sketch out the API endpoints and the database that will support our system.
API Design
A simple RESTful API is perfect for this. We’ll need a few key endpoints:
POST /api/v1/shorten: The request body contains thelong_urland optionalcustom_aliasandexpiry_date. On success, it returns a201 Createdwith the new short URL. If a custom alias is taken, it returns a409 Conflict.GET /{short_code}: This is the main redirection endpoint. It should return a301 Moved Permanentlyredirect to the long URL. If the code is not found or has expired, it returns a404 Not Found.DELETE /api/v1/url/{short_code}: Deletes a short link, returning a204 No Content.PUT /api/v1/url/{short_code}: Updates a link (e.g., its destination), returning a200 OK.
For more on API best practices, check out our (internal) guide on REST API fundamentals.
Database Schema
We need a table to store our links. A simple SQL table would work initially, but given our scalability requirement, a NoSQL database like Cassandra or DynamoDB is often preferred for its horizontal scalability and high write throughput.
Regardless of the choice, the schema would look something like this:
| Column Name | Data Type | Description |
|---|---|---|
short_code | VARCHAR(10) | Primary Key. The unique 7-10 character code. |
long_url | TEXT | The original URL destination. |
expiry_date | DATETIME | The timestamp when the link should expire. |
created_at | DATETIME | When the link was created. |
user_id | VARCHAR(36) | The ID of the user who created the link (optional). |
Step 4: The Magic Trick – Engineering the Perfect Redirect
The redirection flow is the most critical and frequently used part of our system. It needs to be incredibly fast.
Here’s the step-by-step process:
- A user clicks a short link, e.g.,
our.domain/qX0. - The request hits our
GET /{short_code}endpoint. - The application server takes the
short_code(qX0). - It first checks a high-speed cache (more on this in the next step).
- If not in the cache, it queries the database for the record where
short_codeequalsqX0. - If found and not expired, the server retrieves the
long_url. - The server immediately issues an HTTP
301 Moved Permanentlyredirect response to the user’s browser, with thelong_urlin theLocationheader. - The user’s browser automatically navigates to the long URL.
This entire process should happen in milliseconds.
Step 5: Preparing for the Flood – A Scalable URL Shortener Design
Our basic URL shortener design works, but it won’t survive millions of users. The database would become a bottleneck. To handle the scale, we need to introduce more components.
- Load Balancer: A load balancer will sit in front of our application servers. It distributes incoming traffic across multiple servers, preventing any single server from being overwhelmed. This is the first step to achieving high availability.
- Caching Layer: Since link redirection is a read-heavy operation (far more reads than writes), a cache is essential. We can use an in-memory datastore like Redis or Memcached. The flow becomes: check Redis for the
short_code. If it’s a “cache hit,” return the long URL immediately. If it’s a “cache miss,” query the database, then store the result in Redis for future requests. This dramatically reduces database load. - Database Replication: To further scale our database, we can use a primary-replica model. All writes (creating new links) go to the primary database. These changes are then replicated to multiple read-replica databases. All read requests (redirections) are served by the replicas, distributing the read load.
Step 6: Thinking Globally – Advanced Design with CDNs
For a global user base, latency is dictated by the physical distance between the user and our servers. A user in Europe accessing a server in the US will experience a noticeable delay. This is where a Content Delivery Network (CDN) comes in.
We can apply CDN principles to our URL shortener design.
- We place cache servers (often called “edge nodes”) in various geographical locations around the world (e.g., London, Singapore, Sydney).
- When a user in Europe clicks a link, they are routed to the nearest edge node in London.
- If the London cache has the link, it redirects instantly.
- If not, the London node queries our central database (in the US), gets the long URL, serves it to the user, and caches it for all future requests from that region.
This geo-distributed caching model drastically reduces latency and provides a snappy experience for everyone, everywhere. For a great explanation of how CDNs work, check out Cloudflare’s guide.
Step 7: The Grand Vision – A Complete System Architecture Diagram
Let’s put all the pieces together into a final, high-level architecture.
- User Request: A user’s browser sends a request for a short URL.
- DNS & CDN: The request is first routed by DNS to the nearest CDN edge node.
- Edge Cache: The edge node checks its local cache. If there’s a hit, it redirects immediately.
- Load Balancer: If it’s a miss at the edge, the request is forwarded to our central infrastructure, starting with the Load Balancer.
- Application Servers: The Load Balancer directs the request to one of several stateless application servers.
- Distributed Cache: The application server checks our central distributed cache (Redis). If it’s a hit, it returns the data.
- Database: If it’s a miss, the server queries one of the database read replicas.
- Response: The
long_urlis returned up the chain, getting cached at each level (central cache, edge cache) along the way before the final redirect is sent to the user.
This robust architecture ensures that our URL shortener design is scalable, resilient, and incredibly fast.
Conclusion
Designing a URL shortener is a journey from a simple idea to a complex, distributed system. We’ve seen that by carefully considering requirements, choosing the right generation strategy, and strategically layering components like load balancers, caches, and CDNs, we can build a system capable of serving a global audience.
This step-by-step process is a powerful template for tackling not just this problem, but any system design challenge. It teaches us to start small, identify bottlenecks, and scale intelligently. So, the next time you click a short link, you’ll know the incredible engineering that makes that instant redirect possible.
Discover more from teguhteja.id
Subscribe to get the latest posts sent to your email.

