System design · Day 10
Capstone: design a URL shortener from first principles
Bring requirements, APIs, storage, caching, queues, scaling, and reliability together in one complete beginner design.
A capstone is useful because system design is not a list of components. It is a chain of decisions, where every choice should trace back to a requirement.
Define the contract and estimate the load
Our service accepts a long URL, returns a short code, and redirects that code quickly. We need unique codes, durable mappings, optional expiration, and basic abuse protection. Following Day 2, assume one million new links and one hundred million redirects per day: reads outnumber writes by roughly one hundred to one.
Expose POST /links to create a mapping and GET /{code} to redirect. The create path validates the URL, generates a random base-62 code, and writes it to storage. The redirect path must remain extremely small because it is the hot path.
Choose the data and request paths
A key-value model fits naturally: short code as the key, destination and metadata as the value. A relational database is still a sensible starting point because it offers uniqueness constraints and operational familiarity. Add a cache from Day 6 for popular codes, using cache-aside and a short negative cache for missing codes.
Traffic reaches a load balancer, then stateless application instances. A cache hit returns the destination immediately; a miss reads the database and fills the cache. Click analytics go to a queue from Day 7 so redirects do not wait for reporting work.
Scale carefully and design failure
Replicas can serve read-heavy traffic, while partitioning by a hash of the short code spreads data if one database no longer fits. Code generation must avoid collisions across instances; random codes with a uniqueness check are simpler than a global counter and scale well enough for this workload.
Apply Day 9's reliability checklist: timeouts between services, retries only for safe operations, dashboards for redirect latency and error rate, database backups, and a tested recovery plan. The final architecture is not correct because it uses many boxes. It is correct when each box solves an identified pressure.
“A good design is a story in which every component has a reason to exist.”