Software System Design Simplified for Beginners
What is System Design?
System design refers to defining system elements to satisfy the requirements of a translated problem. The elements in this context refer to the architectural design, modules, components, and interfaces. Don’t fret, I’ll explain these concepts further.
Element of System Design
- Architectural Design: The architectural design is a conceptual model that defines the structure, behavior, and views of a system, considering the different types of users required for your system. For example, in designing an e-commerce system, the customer’s interaction and view of the app would differ from the manager’s or the delivery person’s.
- Modules: A module is a group of components that handles a specific task or function in a system.
- Components: Components are the individual parts that make up a module.
- Interfaces: An interface is a point that facilitates communication (data exchange) between two systems. For example, API (Application Programming Interface) is an endpoint that allows communication between a client and server. Clients query data on a server through requests, and the servers process and respond to these requests.
Why System Design?
System Design offers a problem-solving approach to implement large-scale distributed systems. Large-scale means intensive usage with very high performance expectations. A distributed system means the server or code executing the program is not centralized but can be found in separate locations.
A great example of a large-scale distributed system is Google Maps, which supports daily millions of end-users and third-party apps like Bolt. For such a system to operate successfully, its design must account for problems such as a rapidly increasing user base where millions of users need real-time results, map accuracy, server crashes, and more.
System Design provides the blueprint to overcome design problems and achieve sustainable systems by leveraging design patterns.
What are Design Patterns?
Design patterns are general reusable solutions to a commonly occurring problem within some given context in software designing.
For example, consider the problem of delivering YouTube content to millions of subscribers instantly without overloading your servers. A similar problem can occur with other social networking or streaming platforms. A reusable approach like the Publisher-Subscriber model can be leveraged to solve this. This is how design patterns are utilized to solve recurring system design problems.
Some other common design patterns are Load Balancing, Database Sharding, CDN (Content Delivery Network), and Caching.
Practical Approach to System Design
Knowing the Objectives of System Design
System design aims to achieve:
- Practicality: The system should be feasible, implementable, and sufficient for the targetted user base.
- Accuracy: The system should achieve nearly all its functional and non-functional requirements (read further in the requirements gathering and analysis phase).
- Efficiency: The system should have a high throughput. It should effectively utilize resources to produce timely results.
- Reliability: The system should be dependable and highly available. System breakdowns, breaks in services, and loopholes must be avoided.
- Completeness: The system should meet all user and system requirements (read further in the requirements gathering and analysis phase).
- Optimization: The system must be designed to attain the best possible results from all feasible solutions under the given circumstances.
- Scalability: The system should be flexible and adaptable to changing user needs and performance requirements.
Steps in System Design
(Note: Requirements Definition should be done during the Requirements Gathering and Analysis Phase of the Software Development Lifecycle. )
- Requirements Definition: Define requirements from the users’ perspective. This can be broken down into functional and non-functional requirements. Functional requirements are the functions implemented to enable users to complete their tasks. Non-functional requirements refer to designing the system to achieve the objectives defined earlier.
This requires thinking through issues incrementally and systematically. Visually represent your thoughts and note down key things to your approach. Ask questions to resolve uncertainties. Uncertainty inhibits problem-solving. - Core Features and System Architecture: Prioritize features vital to the system’s core functionality and define your architecture to correctly represent these features and how they relate to other features within your system. Architect your system to meet the predefined functional and non-functional requirements. Ensure flexibility, extensibility, and fault tolerance.
- Data Definitions: Breakdown features for your system into data definitions. Think about how features can be stored or represented in your database. Eg. In designing a blog app, a commenting feature will require an ID to identify the comment, a reference ID that represents the user who created the comment, and the comment defined as a string data type.
- System Interface: Design reusable endpoints or APIs to communicate with the defined data in your system. Think about how best external users can query data on your server and how best your server can handle multiple requests in real-time.
- User Interface and Usability: Define and visualize how different user groups interact within your system.
- Testing: Prototype and run tests with different use cases to analyze whether the system is feasible before going into coding. Assess design options and review approach.
Design Trade-offs
Common factors that affect trade-offs in System Design:
- Time: To avoid inconveniences to your stakeholders, it is critical to design your system considering your time factor to deploy. Following a microservice architecture is a great way to frequently deploy and improve solutions.
- Cost: Design your system to utilize resources as effectively as possible.
- Performance: Choose technologies and leverage best practices like caching, parallel processing, and load balancing to meet the performance needs of your system.
- Scalability: Think long-term. The system should be flexible to change and easily be improved upon. Scalability can be achieved by using cloud computing, distributed systems, and a microservice architecture.
- Complexity: Complexity leads to confusion, errors, delays, and inefficiencies, so it is important to apply principles and practices that reduce and manage complexity.
- Security: Improve your system security by using techniques such as encryption, validation, authentication, authorization, auditing, logging, and testing.
As engineering/development practices are continuously refined in a digitally changing world, it is vital to note there is no perfect engineering solution. Nevertheless, system design provides a logical approach by leveraging design patterns to overcome these design problems in order to achieve reliable and efficient systems that can be easily modified and improved upon.