
Design bit.ly
Designing systems using hellointerview.com
For each portion, I’ll regurgitate the feedback I received. Each system design interview is different, so it’s interesting to see how hellointerview has tailored their AI feedback system.
Requirements
Functional requirements
- Users should be able to submit a long URL and receive a shortened version.
- Users can optionally provide a custom alias and/or expiration for the shortened URL.
- Users should be redirected to the original URL when using the shortened URL.
Non-functional requirements
- low-latency reads (<200ms) redirect when accessing original URL from shortened URL
- data consistency when shortened url’s map to original url
- high availability for url’s to be usable
- we should not and can not lose url’s (that’d be an unfortunate user experience)
- scalability for url storage
- uniqueness of short url’s
Requirements: takeaways
You demonstrated a strong understanding of non-functional requirements, improving your design by addressing critical aspects like latency and uniqueness of URLs.
- In future designs, ensure you identify and address all critical non-functional requirements, such as data integrity and system reliability.
- Quantifying performance metrics, like latency, can provide clearer targets and improve the precision of your design.
- Contextualizing requirements within the specific needs of the system helps in creating a more relevant and robust design.
Data integrity and system reliability
I need to elaborate on fault tolerance in my non-functional requirements. In order to do that, the specificity of my requirements becomes imperative.
What could I have said instead?
- high availability for url’s to be usable → high availability (99.999%) for url redirection
- we should not and can not lose url’s → URL’s must be preserved without loss
- data consistency when shortened url’s map to original url → the mapping between shortened url’s and original (or long) url’s must remain consistent and unique
Even one word can change or improve the clarity of a statement or requirement.
Core Entities
- Url
- User
Lately, I’ve been trying out starting with the Core Entities section in order to get a better understanding of what I’m even building. It’s like building out the components or characters of a story, while the requirements help guide the story along.
API
// shorten a URL
POST /urls -> Url
{
original_url,
alias: alias?,
expiration: expiration?
}
Url: {
original_url: "https://averageprogrammer.com",
short_code: "https://avgprg.com",
alias: null,
expiration: null
}
// redirect to original url
GET /urls/{short_code} -> 302 redirect
High-level design and deep dive
I’ll put these together since I don’t have the initial design I made. But, I think the takeaways are important.
Starting with the high-level design, the following are the takeaways.
Clarity of core processes
The feedback emphasized how important it is to clearly outline each core process—like URL creation, redirection, and analytics—so that anyone looking at the system can grasp the flow at a glance.
Improvement: my diagram may have benefited from labeling the critical paths more explicitly (e.g., creation vs. retrieval). I know that it can get a bit messy, but it’s important to be intentional about communication.
Database schema design
Separating short codes from full URLs helps keep the system flexible and maintainable.
Improvement: I could have shown a more detailed schema (fields, data types) for better clarity. It’s good to get things down, but also great to make sure people know when I said “short_code”, it’s still a string.
Edge cases & error handling
Handling expired links or invalid short codes is essential.
Improvement: since redirection is important, what happens when we can’t find the code? Identifying unhappy paths are essential, especially when getting more complex to cover your bases.
Going on to the deep dive, I got some important feedback I need to really pay attention to.
Race conditions & thread-safety
In a distributed environment, concurrent requests to create short codes could lead to collisions.
Improvement: talking more about how the global counter works. I introduce the global counter, but don’t really say anything about it other than that it should “increment atomically”. That may be fine if we make it a centralized global counter with a single data store since it would support atomic operations (like INCR).
Final thoughts
Designing a system like Bit.ly taught me how crucial it is to balance clarity, scalability, and robustness. If you’re on a similar journey—or just curious about system design challenges—remember that clarity, iterative improvements, and an openness to alternative solutions are absolutely key to building a reliable, maintainable system. This was fun!