- System design is about defining the architecture and components of a large-scale technical system.
- The main principles of system design include modularity, scalability, resilience, security, performance, and availability.
- The key components in system design include servers, storage options, message queues, database design, and containerization.
If you’re a budding developer, or considering a career change to software development, you’ve probably heard someone throw around the term system design. Maybe you have a hunch as to what it means — or you have no clue, but you’re too afraid to ask. Whatever the case might be, you might’ve figured out by now that it’s a concept you need to understand.
In the realm of computer science, system design is about defining the architecture and the minute components that go into making a large-scale technical system. Whether you’re aware of it or not, systems are all around us. If you have a job, you probably interact with your workplace’s internal system. If you use an online service, you interact with a system.
These systems are the product of intentional design — even if that design is poor (which they certainly can be). And if you decide to become an engineer, you will be involved in system design in some capacity.
At the start of your career, you might only bother with one aspect of your company’s system design. The more senior you become, the more high-level your design responsibilities become. Either way, it’s never too early to start getting a handle on what system design is all about.
With that being said, let’s discuss the main principles that go into system design, the technical components of system design, and the tools and products engineers can leverage to make their jobs easier. We’ll even throw in some video lectures from experts in the field to help you understand some of the more complex aspects. Let’s get into it!
The Principles of System Design
First off, if the whole concept of system design seems intimidating, let’s get one thing straight: it does not have to be! While there are plenty of complex and confusing systems out there, starting with something simple and learning its ins and outs from a foundational perspective will get you further than trying to dismantle complexity. Famous system designers, like John Gall, understood this decades ago:
System design is immediately easier once you understand the underlying principles. So, that’s where we’ll start.
You should design a software or database system around modular parameters. That is to say, a well-run system comprises multiple independent modules that interact with one another through well-defined interfaces. This results in a legible, easy-to-maintain, and scalable system — which brings us to our next point.
There’s a good reason you hear the word “scale” all the time in corporate America. Believe it or not, it is not just a buzzword. Since the goal of most businesses is to flourish and grow, every organization needs to make sure that their operations can scale up to meet increased demand.
As businesses mature, their tech stack inevitably becomes more complex and takes on greater volume. Consequently, a system should be designed with scalability in mind. A system should be equipped to handle increased data needs and application usage while experiencing minimal impact on performance and availability.
When should you start thinking about scalability? Honestly, it’s never too early. Early-stage startups might de-prioritize scalability in system design to focus more on other attributes. But ideally, they should have scalability in mind from the outset.
It may sound shocking, but organizations strongly prefer it when their systems don’t fail. Well-managed developer teams have checks and balances in place to make sure that only polished, error-free code makes it into the production environment. These checks and balances are usually written into the team’s documentation, or else it’s passed through oral tradition, much like a Viking clan in days long gone.
Even with fail-safes in place, though, things do sometimes go awry. With that being the case, developers also need to design their systems to weather failures with little to no interruption in availability.
System architects must keep the organization’s resources safe from unauthorized access, modification, or other malicious behavior. Given the scope and severity of today’s cybersecurity challenges, and given that there are plenty of low-cost security tools available on the market today to protect against such threats, there’s really no excuse to be lax on this.
Ideally, your system would run like a Ferrari, not your dad’s antique station wagon. A high-performance system performs even complex operations in seconds — most of the time. Ditto for data queries. The world moves extremely fast these days. So should your system.
Users want to be able to access data and software whenever they please. This is especially true in today’s world of remote work, where you could potentially have colleagues working at 1 AM in Thailand. Engineers meet these realities by designing systems that are flexible, scalable, and highly performant —regardless of when and where users access the system.
The Components of System Design
Okay, so you know you need your system design to be modular, scalable, secure, and performant. But how do you build it? Now that we’ve devoted some energy to understanding the principles of system design, it’s time to dive into the technical aspects. Keep in mind that system design is a vast field with tons of different avenues. Most developers do not touch on every single one of these components in their role.
If you’re early on in your career, though, you should be curious and give yourself broad exposure to the manifold possibilities available to you as a developer. We’re here to help you do that, so we’ll try to be fairly comprehensive without totally overwhelming you.
Regardless of the specific project you’re working on, you’re most likely going to be following the same basic playbook. Having procedures in place gives a sense of order and direction to what could otherwise be a daunting, highly complex initiative. The steps might change, depending on your project, but you can typically break it down into the following steps.
Step 1: Understand the Requirements
When you’re designing a new system or overhauling an existing one, it’s helpful to start by considering what the requirements of the system are going to be. When designing software products, your requirements are going to include things like:
- Performance & Availability
- Error handling
- Programming language(s)
- Operating System
- Local or Network
Step 2: Design the Architecture
Once you understand the requirements, your task is to start brainstorming your system design. This means figuring out what components make up your system, how these components interact with one another, and how data flows through the system.
For instance, if you’re building a web application, you’ll likely want to include elements like a DNS (domain name system), load balancing (to handle high web traffic), and caching (replicating data across servers). These are not all of the components that would go into your architecture, but they are some of the fundamental building blocks.
Step 3: Design the Modules
Assuming that your approach to system design is modular, then you’re going to want to start breaking down your overall system into small, independent modules. Each module performs a highly specific function and integrates with other modules in your system.
Perhaps the most popular iteration of modular design is microservice architecture, which involves structuring an application with loosely coupled services that are independently developed, deployed, and maintained. Taking a modular design approach comes with many benefits, including reducing costs and ensuring scalability and flexibility.
Step 4: Design the Interfaces
Once you’ve established the components that make up your system, the next step involves determining how the components — or modules — relate to one another. How do they interact? Designing interfaces includes articulating how data is exchanged between services and defining error-handling mechanisms so as to minimize system failures.
Step 5: Design the Data
Finally, you need to design your system’s underlying data architecture. This means determining how your system’s data is structured, the schema used to organize data sets, and the parameters through which users can access and manipulate data.
Key Components in System Design
Now that you understand the fundamental building blocks of system design, you can start getting acquainted with some of the most popular components that developers use to build various systems.
The features covered here run the gamut from setting up servers and caches, defining storage parameters, establishing message queues, designing databases, and setting up containers.
Odds are your system is going to have some kind of relationship with the Internet. Whether you’re running a site, building a web application, or performing computations on the cloud, you need to manage how users access the internet, and managing servers is key to that.
A proxy server is a popular way of establishing a channel between a user and the internet. By forwarding user requests to a site, proxy servers separate the user from the site they’re accessing. Creating such a barrier is actually a good thing, as it improves the site’s security and protects the user’s privacy. Proxy servers also facilitate data caching so that data doesn’t need to be constantly reloaded every time a user visits a site, thus boosting site speed.
If data is indeed everywhere, you’ll need to articulate a reliable way of storing data within your system. The technique you use to store data depends on the needs and scale of your system. Here are some popular options:
- Block storage
This involves breaking down your data into equally sized blocks, giving the blocks each a unique name, and then sending the blocks to physical storage. Block storage puts emphasis on efficiency and ease of access to stored data.
- File storage
File storage is a hierarchical method of storing data in folders that are then placed in directories. It’s a somewhat old-school way of storing structured data that works well for smaller organizations whose teams aren’t technically inclined. The bigger the organization though, the more unsustainable this storage method becomes.
- Object storage
This method works well for systems with huge datasets, as object storage is designed to handle vast amounts of unstructured data. Its flexibility and scalability lend itself especially well to archiving and creating backups.
In this article, we’ve made repeated mention of modularity in system design, and we’ve alluded to the fact that these modules need to be able to communicate with one another. That’s where message queues come in.
In system design speak, message queues describe the process of notifying relevant modules when a certain action is complete. The notification usually acts as a trigger, alerting a module that it’s time to take action on something.
A popular use case for message queues is in data pipelining, where data streams through multiple processing centers before being made available to the end user.
Any organization working with large datasets is likely going to need to use databases to keep track of proprietary data. The big decision that system architects need to make is this: should we go with a relational database, a non-relational database, or do we perhaps need both?
A relational database organizes data into tables of rows and columns. So, it is structured. This allows analysts and other stakeholders to access data with a Structured Query Language.
Non-relational databases, on the other hand, are unstructured and have a dynamic schema—meaning they can use many kinds of documents besides just tables of rows and columns. Key-value stores and graph databases are some popular examples of unstructured data.
System design is a complex task that requires working with a lot of code and code dependencies. Wouldn’t it be wonderful if there was some way to efficiently package software code and its dependencies into a reusable, parameterized unit? Fortunately, there is!
Containerization has done to software development what Fordism once did to auto manufacturing. Containers allow developers to reuse code and dependencies across any infrastructure, thus sparing them from having to reinvent the wheel as the organization grows in scale or adopts new technologies.
Developers generally use Docker as a containerization platform for deploying code, and then Kubernetes to manage containers. A popular alternative to Kubernetes, though, is Redhat’s OpenShift, which offers ready-made and customizable containers.
System Design: Key Takeaways
As you can see, system design is a huge undertaking with many nuances, so let’s summarize the main points we’ve discussed here:
- To make system design somewhat less of a daunting task, start with the fundamentals: what system requirements pertain to your organization, and how are you going to design your architecture in such a way that your system is scalable, flexible, highly performant, and available to its users?
- If you take a modular approach to system design (which you probably should), then make sure your modules have a way of interacting with one another. However, keep your modules loosely coupled so that each component has a healthy degree of autonomy.
- Ensure your system adheres to a data management strategy that makes sense for your business context.
- Keep an eye out for ways to make your architecture efficient and responsive to changes over time. Leverage innovative tools like containers and artificial intelligence.
To that last point, be sure you’re aware of industry-leading products in your area that can help you successfully manage your system. For instance, Amazon Web Services has a whole suite of products that facilitate database design, data caching, data pipelining, network management, message queues, cloud computing, and much more.
If you want to jump into a detailed introduction to system design, from start to finish, this series from FreeCodeCamp is one of the most comprehensive lectures on the subject:
The image featured at the top of this post is ©Amnaj Khetsamtip/Shutterstock.com.