Use Clean Code to Pass Your System Design Interview

Jacob on Software
CodeX
Published in
7 min readAug 10, 2021

For this week’s blog on Clean Code, I’m going to do something a little different. While up to this point we’ve discussed ways to keep code in our programs clean, we ought to spend some time thinking about how to expand our programs to be scalable systems. Just like our programs, our systems need to follow a set of standards to facilitate our ability to write software quickly and efficiently. While the questions we ask and solutions we uncover are slightly different at this higher level of abstraction, we can abide by a set of guidelines to incrementally build more complex software artifacts.

Although this Clean Code chapter is chock full of helpful information on designing clean systems, for the purposes of this blog I won’t go into too much detail on the following subjects: Separation of Main (a subset of Separation of Concerns), Factories, Dependency Injection, Domain-Specific Languages, and the incredible work of Christopher Alexander (Sincerely, if you’re serious about system design, I would highly recommend you read Alexander’s The Oregon Experiment. It’s simply one of the best books I’ve ever read on how to design and build a cohesive system). Still, you should take some time to learn more about these topics. They’re great to know for both a system design interview as well as for general knowledge of keeping your code clean.

For this blog, I’ll be drawing heavily on Alex Xu’s practical — and aptly titled — book, System Design Interview. In it, Xu covers both a general approach to solving system design questions as well as common system design interview questions. While the Clean Code chapter on systems invites us to design a city, for the purposes of this blog post we’ll design a URL shortener like Bitly or tinyurl.

Whenever we design a system, we want to follow a general guideline to keep our answer focused and flexible. It’s important to remember, there is no right answer for a system design interview. Interviewers aren’t interested in knowing if you’ve memorized the textbook answer for how to design Google’s software architecture. Rather, they want to see you reason through the needs of your system, ask intelligent questions, handle ambiguity, etc. It’s also important to collaborate with the interviewer, listening to their prompts for what the system should be able to control and taking their advice when offered. Think of it like a miniature pair programming exercise. These general steps for system design should help nudge us down the right path:

  1. Understand the problem and establish design scope
  2. Propose high-level design
  3. Design deep dive
  4. Wrap up

Now that we know the flow of the system design interview, let’s get started!

Understand the problem and establish design scope

There are many different ways to design a URL shortener. By asking relevant, opinionated questions, we scope out how we want our system to behave, which as the interviewee is ultimately up to you. For a URL shortener, some good opening questions might be something like the following:

  • What is the traffic volume of the service?
  • How long is the shortened URL?
  • What characters can be included in the shortened URL?
  • Can shortened URLs be deleted or updated?

For the purposes of this exercise, let’s assume that our URL shortener service takes a long URL and creates a much shorter alias which redirects the user to the long URL address. Let’s also assume that 100 million URLs are created every day, that they contain only alphanumeric characters (0–9, a-z, and A-Z), and that the generated URLs can neither be updated nor deleted. Given these considerations, we want this system to accommodate high availability, scalability, and fault tolerance.

Sample back-of-the-envelope calculations

Once you have a basic understanding of the desired parameters, you can do some quick back-of-the-envelope calculations to get a better sense of how the system should behave. Remember, the interviewer isn’t grading you on correctness here. Rather, she or he is examining your thought process. By making some assumptions about what to expect you show that you understand what the system needs to function well. While it might seem daunting, it isn’t too difficult to assume that you’ll need your write operations to handle 1160 URLs/second (at 100m URLs/day), your read operations will need to handle 11,600 URLs/second (assuming a read to write ratio of 10:1), and that you’ll need to manage storage for 36.5 billion shortened URLs if you want to maintain them for a period of ten years (see below for Xu’s sample calculations for this question). While these are only assumptions, you now have a detailed template on which to build your URL shortener.

Propose high-level design

To be sure, any URL shortener would need to address issues like API endpoints, URL redirecting, and URL shortening, and this is your chance in the interview to flesh out those details. More importantly than the specifics of your high-level design, however, is to avoid the temptation to do a Big Design Up Front (BDUF). As Dr. Kevin Wampler writes in Clean Code, doing a BDUF “inhibits adapting to change.” Unlike physical buildings, software’s ephemeral nature allows us to make radical changes to our design depending on our needs. With a “naively simple” (but still detailed) design, we’re able to build out our software but remain nimble enough to change depending on our evolving circumstances.

h/t xkcd.com

Design deep dive

A key component of any URL shortener is how you’ll implement a hash function. There are a number of different techniques for doing this, but people most commonly use hash + collision resolution and Base 62 conversion. While the details of these two strategies are outside the scope of this blog, Xu gives a very helpful breakdown of the differences between the two procedures:

Other strategies for a design deep dive might include methods for storing a shortURL in the database with a unique ID, as well as caching the hashed values for enhanced speed (given that your system is designed to handle read operations more quickly than write operations).

More importantly than these specific tactics, however, we would be more successful if we keep in mind what Clean Code reiterates again and again: “Growth (in software) is not without pain.” It’s not possible to get a system right the first time, and there will be many little hiccups along the way as you build increasingly intricate, more heavily-trafficked systems. Your interviewer is well aware of this, and rather than trying to account for every possible bug in the system, we would do better to write clean code, test and refactor that code, and slowly, diligently expand our system’s horizons.

Or, as Wampler tells us much more elegantly:

“Software systems are unique compared to physical systems. Their architectures can grow incrementally, if we maintain the proper separation of concerns”

Diagrammed out, our completed URL shortener might look something like this:

h/t kuczma.dev

Wrap Up

If there’s time left in your interview, you can go above and beyond by discussing more advanced aspects of a URL shortener. Delving into topics like Rate Limiters, Web Server Scaling, Database Scaling, and how you could acquire Analytics based on your shortened URLs show that you have a thorough understanding of the problem at hand. Congratulations! You nailed your system design interview!

As Wampler tells us in Clean Code, “systems must be clean too.” When you gain more skills and experience as a software engineer, you’ll steadily be relied upon to build up your system’s architecture. While this gives you a chance to dive into new and interesting topics, lessons learned from Clean Code can support you as you build up more robust software ecosystems.

If there is a main takeaway from Clean Code and Xu, I’d say that it’s important to remember that systems are built step by step. Something as vast and complex as Amazon or Google went through hundreds and thousands of iterations to be able to handle the traffic that they do today. Even a single physical building will go through tweaks and revisions from the design stage to when the final lightbulb is screwed in. In the same way, your systems will take time to build, and you would do well to remember that trial and error are features of the system rather than bugs. I loved the parting words of Wampler’s chapter in Clean Code so much I think it’s a good way to end this post:

“Whether you are designing systems or individual modules, never forget to use the simplest thing that can possibly work.”

--

--