Assume the system handles:
Over 5 years:
Storage estimation:
Storage estimation (per URL ≈ 256 bytes including metadata):
- Daily growth ≈ 4.4 GB
- Over 5 years ≈ 8–10 TB (with buffer)
Bandwidth:
- Write traffic is minimal (~50 KB/sec)
- Read traffic dominates (~5 MB/sec)
Conclusion:
Storage and bandwidth are manageable at scale, but the system is highly read-heavy, so caching (CDN + Redis) is critical to handle high traffic and ensure low-latency redirects.
POST /api/v1/urls
Request body:
Response (201 Created):
Authentication: API key in the Authorization header. Every write request must be associated with a registered account for rate limiting and abuse tracking.
Rate limit: 100 URLs per hour per API key for free tier. Higher limits for paid accounts. Returns 429 (Too Many Requests) when exceeded with a Retry-After header indicating when the client can try again.
Validation: The server should validate that the longUrl is a well-formed URL with a supported scheme (http or https). Reject URLs with unsupported schemes (javascript:, data:, ftp:) to prevent abuse. Optionally, check that the URL is reachable with a HEAD request, though this adds latency and is better done asynchronously.
GET /shortCode
Returns a 302 (Found) response with the Location header set to the original long URL. The browser follows the redirect automatically. No authentication required. Anyone with the short URL can follow it. This is intentional. Short URLs are shared publicly and must work for everyone who clicks them.
If the short code does not exist or has expired, returns 404 (Not Found) with a JSON body explaining the error. For expired links, include a message indicating the link has expired rather than simply saying "not found." This helps users understand what happened and reduces confusion.
For SEO and social media compatibility, the redirect response should also include standard headers: Cache-Control to control CDN behavior, and optionally X-Robots-Tag to tell search engines whether to index the short URL or the destination URL.
This choice has significant architectural implications:
301 (Moved Permanently) tells the browser to cache the redirect. The next time the user clicks the same short link, the browser goes directly to the long URL without contacting the shortener at all. This reduces server load dramatically. The trade-off: you lose visibility into every click. You cannot track analytics, you cannot update the destination URL after creation, and you cannot enforce expiration because the browser never checks back.
302 (Found) tells the browser the redirect is temporary. Every click goes through the shortener. This enables click analytics (you see every redirect), destination updates (change where the link points), and expiration enforcement (the server checks TTL on each request). The trade-off: higher server load because every click hits your infrastructure.
For most URL shorteners, 302 is the right default. The analytics and flexibility outweigh the server load increase, which is handled by CDN caching anyway. The CDN caches the 302 response at edge locations, giving you the load reduction of 301 with the flexibility of 302.
Without rate limiting, a single malicious or misconfigured client could create millions of URLs, exhausting the short code space and consuming storage. Rate limiting protects shared resources and ensures fair usage. Implementation: per-API-key counters with sliding window limits, enforced at the API Gateway layer before requests reach the Shortening Service.
The architecture separates into two distinct paths, each optimized for its unique requirements.
Read (Redirect) path: Client click --> CDN --> API Gateway --> Mapping Service --> Redis Cache --> DynamoDB. Optimized for sub-10ms latency with multiple caching layers. The CDN handles the hottest URLs at edge locations. Redis handles the warm set in the data center. DynamoDB is the durable fallback for cache misses.
Write (Shorten) path: Client request --> API Gateway --> Shortening Service --> Message Queue --> Queue Worker --> DynamoDB. Optimized for reliability and database protection. The Shortening Service generates a guaranteed-unique code from its pre-allocated ID range and enqueues the write. The message queue decouples the client response from the durable write, smoothing traffic spikes.
High-Level Architecture: Read (Redirect) and Write (Shorten) Paths
API Gateway: TLS termination, DDoS protection, request routing, and rate limiting for write requests. All external traffic enters through the gateway, providing a single enforcement point for authentication and throttling.
Shortening Service: Generates short codes from a pre-allocated ID range, validates inputs, and enqueues write requests. Each instance owns an exclusive range of IDs, so codes are guaranteed unique at generation time. Stateless and horizontally scalable behind a load balancer.
Mapping Service: Handles redirect lookups. Checks Redis first, falls through to DynamoDB on cache miss. Also stateless. This is the most latency-sensitive component.
Message Queue (SQS or Kafka): Buffers write requests between the Shortening Service and the database. If 2,000 URL creation requests arrive in one second, the queue absorbs the spike and the Queue Worker drains them at a steady 200 per second that the database can comfortably handle.
Queue Worker: Consumes messages from the queue and writes URL mappings to DynamoDB. Since codes are generated from exclusive ID ranges, collisions cannot occur, and every write succeeds on the first attempt. Rate-limited to match database write capacity.
Redis Cache: Stores the warm set of URL mappings (recently and frequently accessed). LRU eviction policy ensures the cache holds the most useful entries. Cluster mode for horizontal scaling across multiple shards.
CDN (CloudFront): Caches redirect responses at edge locations worldwide. For a viral URL, the CDN serves millions of redirects from a nearby edge node without any request reaching the backend. This is the single most impactful component for read performance.
DynamoDB: The durable source of truth for all URL mappings. Handles cache misses from Redis, supports conditional writes for custom alias conflict detection, and provides automatic TTL-based expiration.
Each component in the architecture has a clear responsibility and can be scaled independently. The CDN scales by adding edge locations (no code changes needed). Redis scales by adding shards to the cluster. The Mapping Service scales by adding stateless instances behind the load balancer. The message queue scales by adding partitions. DynamoDB scales automatically by splitting partitions. No single component needs to handle the full 20,000 reads per second on its own. The CDN absorbs 80-90%, Redis handles most of the rest, and DynamoDB only sees the cold tail of traffic.
Deep dive into 2-3 key components. Explain how they work, how they scale, discuss tradeoffs, capacity, and any relevant algorithms or data structures.
This section focuses on the core challenge of a URL shortener: generating unique short codes at scale without collisions, while ensuring fast lookups through efficient caching and partitioning.
Short codes are generated using Base62 encoding (a-z, A-Z, 0-9), giving 62 possible characters per position. A 7-character code yields ~3.5 trillion combinations—more than enough capacity for long-term growth.
Approaches:
Failure Handling (Important):
To prevent outages, introduce a fallback mechanism:
This ensures continued operation even if the primary generator fails.
Short Code Generation: Hash vs Random vs Range-Based Counter
Even with guaranteed uniqueness, DynamoDB conditional writes act as a safeguard:
A multi-tier caching system optimizes read performance and reduces database load:
Why cache-aside?
Most URLs are rarely accessed. This avoids filling Redis with unused data and maximizes cache efficiency.
Invalidation Strategy:
Both Redis and DynamoDB are partitioned by short code hash, ensuring:
Partitioning: Shard by Short Code (Redis + DB Aligned)
Why short code?
Why not alternatives?
Applied only to URL creation (write path) to prevent abuse.
Behavior:
Implementation:
Backoff: