McDonalds System Design
written by Vera Kong
If you’ve ever taken an intro to psychology course, you should have learned at some point that an effective method of learning and retaining information is semantic association.
What does this mean?
It means you create some sort of association between two or more things/ideas to remember them easily later on. (i.e. you associate coffee as a morning drink to help you stay awake)
TLDR: I took an Intro to Psychology course…
Anyways, enough with the psychology blurb. The reason I’m prefacing this is that I personally remember things/notions with humorous associations. Thus, I present to you a different approach to grasping system design: McDonalds, our favorite fast food chain.
System Design who?
Some technical interviews you might come across as an aspiring Software Engineer are System Design interviews as the title heavily suggests. In essence, this particular style of interviewing is designed such that you are expected to map out an approach to creating an application, like Twitter, that meets the end-user requirements. This usually involves explaining a high-level approach and then diving into the low-level components.
Understanding how System Design interviews work will not only help you ace these types of technical interviews, but also make you an overall better programmer! These interviews could help you be more comfortable with seeing the big picture in software development while exercising invaluable soft skills like communication and openness to feedback.
Obviously, I’m not a Level 5 Staff Engineer or 10x developer, so take this reading with a grain of salt. It should mainly be used in good humor and possibly a better general understanding of system design :D
Let’s Get Down to Business (to defeat the huns):
Data + Databases
Every system has some kind of data being stored and manipulated. In our case, McDonalds serves numerous kinds of fast food items, so let’s use that for our data definitions.
Suppose there are customers who are ordering at the register, then our system should at least describe these orders as such:
- an Order has a unique id and a collection of Food
- a Food is an enum that is either a Burger, Fries, or Soda
This is the only time I’m using the Design Recipe here.
Alternatively, we can characterize this data into relational tables as so:
Great! We’ve come up with our data definition, so now where to store them? Well, since I’ve mentioned relational tables, I’ve set myself up into using a SQL database! If you are familiar with types of databases, then you know that NoSQL databases are also an option.
If you’re new to databases, here’s a cool IBM article explaining what SQL and NoSQL databases are. One big difference between the two types of databases is essentially how they represent persisted data. For instance, SQL databases typically represent pieces of data as “records” or “rows” inside a table as depicted in the image above, whereas NoSQL databases have a bit more flexibility in how data could be stored (key-values, graphs, documents).
Why choose SQL over NoSQL here? A possible argument in the case of McDonalds is that we want to ensure the data we store is always consistent and accurate*. In other words, we want orders to be accurate so that customers will receive the correct items that they requested.
i.e. If I ordered a Happy Meal, I expect to receive a Happy Meal.
SQL databases are perfect for this case then because of its use of tables and favoritism for rigidity.
*It’s important to note that this is not to say NoSQL databases are always inconsistent and inaccurate in comparison to SQL databases. To clarify, NoSQL databases may sometimes return inconsistent data, but this is the price paid to have highly available databases. That is something SQL databases fail to meet in favor of producing consistent data at all times. This concept actually goes into the CAP theorem for databases!
I want a Happy Meal
Ok, so now we have a way to define and store orders. Let’s flesh out a scenario: we’ll have a cashier at the register with various cooks behind the scenes (1 cook each for the fries, burger, and soda station). Every customer walking into McDonalds will give their order to the cashier. In particular, every customer is ordering a Happy Meal 😀
In terms of System Design, you can imagine every customer as a user/client interacting with our McDonalds system via the cashier. The cashier is analogous to a server that receives some sort of API request aka an order for a Happy Meal.
Below is a visual representation of the flow we have so far:
Ok, so we’ve generalized a pretty simple way of how our McDonald system works. We can now work off of this and dive deeper by improving/expanding different parts. There’s a ton of concepts we can talk more about, but for simplicity’s sake, we will mention a few topics.
One area that comes to mind is considering what will happen to our system when more than 1 customer gives their order. What happens when 10 customers show up? 100? 10,000?
You can imagine that this is a ton of customers — kinda like how Steast is when it’s dinner time and it’s the only dining hall open D:
Steast line taken from Reddit
In effect, a lot of customers means a lot of orders are trying to be sent through the 1 cashier. We call this bottlenecking, when a point in the system is heavily congested and creates delays.
Of course, we would like to take in all these orders, so that our McDonalds establishment can make profit and prosper. However, our system in its current state cannot handle tons of data. There are 2 options to solve this: we can either vertically or horizontally scale our system.
How do these methods work?
Vertical scaling refers to pooling all of your resources into a singular machine.
Horizontal scaling refers to adding more machines to the resource pool.
With McDonalds, you can imagine that vertical scaling will require that our cashier has been given the necessary resources to process all the Happy Meal orders such as a superpowered register capable of taking 20 orders at once . We can call this cashier a chungus McDonalds worker.
On the other hand, horizontal scaling will instead require hiring more cashiers with more registers to distribute the many, many customers evenly across. We say that we’re fixing a understaffing issue then.
Now that you know how scaling works, we can see that we would probably choose to horizontally scale our McDonalds system. This is cause, well, you don’t really see 1 person working at McDonalds, right? You see there’s a whole staff of cashiers and cooks, typically.
Here’s a Froakie using the move Double Team
Sick, we’ve scaled our system so that it can handle a ton of customers trying to order their Happy Meals. Although we covered scaling in one area, you can usually apply scaling in other areas as well like hiring more cooks to man the different stations.
Another area we could consider to improve our McDonald’s business is setting up a cache.
But what’s a cache exactly?
Caches, according to stackoverflow, are just high-performance stores, usually memory, with stored data we could retrieve quickly. In layman terms, it’s sort of a secondary database where we can store certain types of data to provide faster access.
Some examples of caches are Redis and Memcached.
Ok, but why do we need a cache?
Here’s one use case for our McDonald’s business: say we have a customer named Ronald who always orders fries and a soda every day. The cashier could just take Ronald’s order every day and store it in our SQL database. When we need to process Ronald’s order, we would have to get the order all the way down from the database and then return it all the way back up.
But this method is quite long because now we need to wait a bit for the database to find the specific order and hopefully return this data back to the cashier in a timely manner. This process may take a few moments or even a couple minutes depending on how much data is being searched and latency between the cashier and database. You can imagine this becomes even longer when there are, for example, 100 customers who always order the same meal.
This is where a cache can come in particularly useful: we can implement a cache that could store the frequently accessed pieces of data. This way we can retrieve the data much faster and quite easily.
For McDonalds, you can imagine the cashier has a brain and a brain stores memories. Then, if the cashier sees Ronald order the same meal of Fries and Soda within the last few days, then cashier will likely remember Ronald’s order for the next time around.
Ever heard of Pub/Sub?
Amazing, we got a cache set up and ready to go to save time and effort when regular customers order at our McDonalds.
We actually have a problem that we didn’t really cover in our McDonalds establishment: how are the orders being made and served?
Right now, we didn’t really set up a system for the cooks in our staff to know when these orders are being put in and need to be made. We could have the cashiers record all the customers’ Happy Meal orders and verbally tell the cooks at the Burger, Fries, and Soda station what to make. However, this is largely inefficient because there’s an heavy dependency between the cashiers and cooks. We want some way to decouple this relationship and streamline our Happy Meal orders.
In fact, there’s a really neat idea we can implement called the Pub/Sub Model!
Pub/Sub is short for publish-subscribe and it’s a messaging pattern where there are senders who publish some sort of info (usually announcements or changes) to a topic and everyone who are subscribed to that topic will receive the same info across.
Some applications that model Pub/Sub are Kafka, Redis, ****and RabbitMQ.
A real life example would be people who have a subscription with Vogue magazine as the topic and every month a new issue is published and distributed among subscribers to read.
Back to McDonalds, how would this work in our establishment?
Hypothetically, let’s say our business tycoon installs monitors at every food station, and these monitors display the recent orders being taken from the cashier.
Now, our cooks at the burger stations just need to take the Happy Meal orders from their monitor that is listening to changes from the register/cashier. This applies to the fries and soda stations as well!
Amazing, now we’ve made our cooks self-sufficient without having to rely too much on the cashier who is recording the customer’s order.
The last of the sample topics that I will go over are load balancers.
Load balancers are basically as the name suggests: they balance the load of data across several servers/machines.
Having some load balancers are really useful when your system needs a way to distribute the load or requests in an efficient manner without creating another bottleneck situation that we tried to avoid in our earlier example in scaling. In fact, implementing some load balancers into our system is a prime example of horizontal scaling!
Typically, we would have load balancers as a layer between users and servers as illustrated below:
We can apply this same concept to our business tycoon: customers are sent to wait in line at certain cash registers.
There are a few ways a load balancer can work like round robin, least connections, or Source IP Hash.
We can briefly talk about using the round robin technique, where requests are distributed in rotation. For example, the 1st request goes to the 1st server, the 2nd request goes to the 2nd server, the 3rd request goes to the 3rd server, the 4th request goes to the 4th server, the 5th request goes to the 1st server, and so on. (This assumes we only have 4 servers active).
In this way, our customer lines at McDonalds are evenly distributed across the cashiers, ensuring we can consistently take all the Happy Meal orders.
You made it to the end of this reading! Amazing. We’ve built together a conceptualized system where our McDonalds can handle a large number of customers coming in and orders being processed to serve. We’ve scaled it enough to avoid some bottlenecks with certain techniques like the load balancing we just talked about.
It’s good enough to say that our business tycoon can become an unstoppable monopoly 😈
Altogether, this is what our McDonalds system might look like:
This reading is quite long, so good job getting through it. Don’t worry too much if you don’t quite understand some of the key terms or concepts; a lot of it comes from exposure and experience over time. There’s a whole ton of areas that wasn’t covered in this article because it would have taken too much time to get into the nuances. For example, I never really talked about tradeoffs of certain decisions or going into detail for real examples of applications. The purpose of this article was really to introduce a broad understanding of what you might expect in a system design.
Of course, I highly encourage doing your own research into things because a lot of this stuff can be interesting. For instance, I mentioned SQL and NoSQL databases, but there is a vast array of options that could be better or worse for your system. There are SQL databases like MySQL and Postgres and NoSQL databases like MongoDB, Neo4j, and Redis. Each of these are really useful in their own ways and it’s worth taking a deeper dive in them to determine what’s best for you.
If you want to read more about system design, I would recommend on looking over the links below and even taking a look at how companies like Twitter or Slack do it themselves.