When an interviewer asks you to design a URL shortener, they’re looking for how you translate a familiar product into a scalable system. The problem is small enough to fit on a whiteboard, yet it opens doors to discuss hashing, databases, caching, and observability. Below is a practical walk‑through you can follow in a real interview.
1. Clarify the scope
Start by asking clarifying questions. Typical ones include:
- What operations are in scope? Usually
createShortUrl(longUrl)andredirect(shortCode). Some interviews add analytics (clicks,geo), custom aliases, or expiration. - What latency expectations? Users expect a redirect in a few hundred milliseconds.
- What traffic patterns? Is the service read‑heavy (many redirects) or write‑heavy (burst of new links)?
- What consistency model? Strong consistency is not required for redirects; eventual consistency is acceptable for analytics.
Having concrete answers guides the rest of the design.
2. Functional and non‑functional requirements
| Requirement | Details |
|---|---|
| Create short URL | Input: long URL, optional custom alias, optional expiration. Output: short code (e.g., abc123). |
| Redirect | Given short code, return HTTP 301/302 to original URL. |
| Analytics | Track click count, timestamp, referrer, optional geo. |
| Scalability | Handle high read volume (many redirects) and moderate write volume (new links). |
| Availability | Service should remain up even if a node fails; graceful degradation for analytics is fine. |
| Latency | < 200 ms for redirect, < 500 ms for creation. |
| Durability | Persistent storage for mappings; loss of a few recent clicks is tolerable. |
| Security | Prevent malicious URLs, rate‑limit abuse, and avoid open redirects. |
3. Core entities and API
// Request to shorten a URL
type CreateRequest struct {
LongURL string
CustomAlias string // optional
Expiration time.Time // optional
}
type CreateResponse struct {
ShortURL string
}
// Request to resolve a short code
type ResolveRequest struct {
ShortCode string
}
type ResolveResponse struct {
LongURL string
// HTTP redirect performed by the gateway
}
Typical REST endpoints:
POST /api/v1/shorten→ returns short URL.GET /{shortCode}→ gateway looks up and redirects.GET /api/v1/analytics/{shortCode}→ optional analytics view.
4. High‑level architecture
+----------------+ +----------------+ +-------------------+
| Client App | ---> | API Layer | ---> | Service Layer |
+----------------+ +----------------+ +-------------------+
| |
v v
+----------------+ +-------------------+
| Cache (Redis)| | DB (SQL/NoSQL) |
+----------------+ +-------------------+
- API Layer: Handles HTTP, validation, rate‑limiting, and authentication.
- Service Layer: Core business logic – generating codes, persisting mappings, updating analytics.
- Cache: Stores hot short‑code → long‑URL pairs to meet latency goals.
- Database: Durable store for all mappings and analytics. A relational DB works well for uniqueness constraints; a NoSQL store can be used for high‑write analytics.
5. Deep dive: Generating short codes
5.1. Simple approach – Base‑62 encoding
- Maintain a monotonic counter
id. - Encode
idin base‑62 (characters0‑9a‑zA‑Z). - Result is the short code.
Pros: Easy to implement, guarantees uniqueness, ordered codes. Cons: Predictable; can be enumerated, which may expose usage patterns.
5.2. Hash‑based approach
- Compute a hash (e.g., MD5, SHA‑256) of the long URL.
- Take the first N bits and encode in base‑62.
- Check for collisions; on conflict, append a salt and retry.
Pros: Non‑sequential, harder to guess. Cons: Collision handling adds complexity; requires extra reads.
5.3. Custom alias handling
If the user supplies a custom alias, verify uniqueness in the DB before persisting. Store a separate index for custom aliases to keep lookup fast.
6. Scaling the read path
Redirects dominate traffic. To keep latency low:
- Cache hot entries: Use an LRU cache (Redis) for the most accessed short codes.
- Read‑through pattern: On a cache miss, fetch from DB, populate cache, then serve.
- Sharding: Partition the keyspace by short code prefix. Each shard can be a separate DB instance, reducing contention.
- Consistent hashing: Allows adding/removing shards without massive re‑balancing.
7. Hard parts and trade‑offs
| Hard part | Typical solution | Trade‑off |
|---|---|---|
| Collision avoidance | Use a counter or check‑then‑insert with unique constraint. | Counter gives predictable codes; hash gives privacy but needs extra reads. |
| Hot‑spot mitigation | Cache, sharding, and load‑balancing across replicas. | More components increase operational overhead. |
| Analytics storage | Append‑only log (e.g., Kafka) + batch processing to a columnar store. | Real‑time view is harder; eventual consistency is acceptable. |
| Expiration handling | TTL on DB rows and periodic cleanup job. | Deleting rows can cause fragmentation; background compaction needed. |
| Abuse prevention | Rate limiting per IP, CAPTCHA for suspicious patterns. | Adds latency for legitimate users; balance strictness vs. UX. |
8. Follow‑up questions interviewers often ask
- How would you support custom domains (e.g.,
go.mycompany.com)? – Add a domain‑to‑namespace mapping and store it with each short code; route based onHostheader. - What if we need to change the length of short codes later? – Use versioned prefixes; older codes stay valid, new ones use longer encoding.
- How do you ensure durability of the mapping? – Replicate DB across AZs, enable point‑in‑time recovery, and write‑ahead logs.
- How would you bulk‑import existing URLs? – Batch generate IDs, use bulk insert APIs, and back‑fill caches.
- Explain the impact of eventual consistency on analytics. – Click counts may be slightly stale; acceptable for dashboards, but not for billing.
9. Sample answer snippet (45‑90 seconds)
"To build a URL shortener, I’d start by defining the core operations: creating a short link and redirecting. For creation, I’d generate a short code using a monotonic counter encoded in base‑62, which guarantees uniqueness without collisions. The mapping is stored in a durable key‑value store—SQL works well because it enforces a unique constraint on the short code. For redirects, the service first checks an in‑memory cache like Redis; a hit returns the long URL instantly, while a miss falls back to the DB and then populates the cache. To handle high read traffic, I’d shard the database by short‑code prefix and use consistent hashing so adding nodes doesn’t cause massive re‑balancing. Analytics are collected asynchronously via a message queue and processed into a columnar store for reporting. Finally, I’d add rate‑limiting and a simple abuse check to prevent spam. This design meets low latency, high availability, and durability requirements while staying simple enough for a typical interview timeframe."
10. How to practice this
- Sketch the diagram on a whiteboard: Write out the components, data flow, and where each request lands.
- Explain code generation choices: Prepare a short comparison of counter vs. hash methods and be ready to discuss collisions.
- Run a mock interview: Use Call Assistant to read your answer aloud, keep the conversation on point, and ensure your story aligns with experiences on your resume.
FAQ
- What is the simplest way to generate a short URL without collisions? Use an auto‑incrementing integer and encode it in base‑62. The database’s primary‑key constraint guarantees uniqueness.
- Why do we need a cache for redirects? Redirects dominate traffic; caching hot short‑code → long‑URL pairs reduces DB load and keeps latency under the typical 200 ms target.
- How can we support analytics without slowing down redirects? Log each click to an asynchronous queue (e.g., Kafka) and process the data in the background. The redirect path remains fast because it doesn’t wait for analytics writes.
- What are common abuse vectors for a URL shortener and how to mitigate them? Attackers may generate massive numbers of links or use the service for phishing. Rate‑limit per IP, require CAPTCHA for suspicious patterns, and validate URLs against known blacklists.
Frequently asked questions
What is the simplest way to generate a short URL without collisions?
Use an auto‑incrementing integer and encode it in base‑62. The database’s primary‑key constraint guarantees uniqueness.
Why do we need a cache for redirects?
Redirects dominate traffic; caching hot short‑code → long‑URL pairs reduces DB load and keeps latency under the typical 200 ms target.
How can we support analytics without slowing down redirects?
Log each click to an asynchronous queue and process it in the background. The redirect path stays fast because it doesn’t wait for analytics writes.
What are common abuse vectors for a URL shortener and how to mitigate them?
Attackers may generate massive numbers of links or use the service for phishing. Rate‑limit per IP, require CAPTCHA for suspicious patterns, and validate URLs against known blacklists.
#system design#url shortener#scalability#architecture#interview prep#a URL shortener