Requirements
Functional Requirements:
- Verified Fact: The problem requires two core operations: create a short URL and resolve a short URL.
- Verified Fact: The core data model is a mapping from a unique short code to a long URL plus metadata.
- Verified Fact: The redirect path is latency-sensitive because it sits directly in front of end users.
- High-Probability Inference: Reads will usually be much higher than writes.
- High-Probability Inference: Traffic will be skewed. A small set of links will become hot.
- High-Probability Inference: Analytics are valuable, but they should be asynchronous, not in the redirect hot path.
- Unknown: QPS, storage horizon, number of regions, whether links can expire, whether aliases are mutable, whether custom aliases are required, whether identical long URLs should deduplicate, and how aggressive anti-abuse rules must be.
I am not inventing traffic numbers. Without them, the honest design is a solid baseline that scales in the standard read-heavy case.
Non-Functional Requirements:
- Low latency. Redirects should usually complete with one cache lookup, or one key lookup on a primary store on cache miss.
- High availability. Redirects matter more than creation. If analytics fails, redirects must still work.
- Scalability. The read path must scale horizontally. The write path must scale enough to avoid a central bottleneck.
- Durability. URL mappings cannot disappear after a node failure.
- Consistency. After a successful create response, the short URL should be resolvable immediately or near-immediately.
- Reliability. Cache failure should degrade to the database, not take the service down.
- Security and abuse resistance. Validate URLs, rate-limit creation, block dangerous schemes, and support takedowns.
- Observability. Track redirect latency, cache hit rate, create failures, error rates, and abuse signals.
Priority order for a normal public shortener: redirect latency, redirect availability, mapping durability, horizontal scale, abuse handling, analytics.
API Design
Use a clean split between creation, redirect, and metadata lookup.
POST /v1/urls
{"long_url": "https://example.com/some/very/long/path","custom_alias": "spring-sale","expires_at": "2026-12-31T23:59:59Z"}
Success response:
{"short_code": "b9K2xQ","short_url": "https://sho.rt/b9K2xQ","long_url": "https://example.com/some/very/long/path","expires_at": "2026-12-31T23:59:59Z"}
Errors:
- 400 invalid URL
- 409 custom alias already taken
- 429 rate limited
GET /{short_code}
Behavior:
- If found and active: return 302 Found with Location: <long_url>
- If not found: 404
- If expired or disabled: 410 or 404, depending on product policy
GET /v1/urls/{short_code}
Purpose:
- Returns metadata without redirecting
- This directly satisfies the requirement to return the long URL for a short URL
Example response:
{"short_code": "b9K2xQ","long_url": "https://example.com/some/very/long/path","status": "active","created_at": "2026-04-02T10:00:00Z","expires_at": null}
One direct design point: I would not assume that the same long URL must always return the same short URL. That is a product decision, not a system-design fact.