Writing An Efficient Software Design Document
In this article, I’ll be talking about a typical process that I follow to thoroughly understand a problem and successfully deliver a solution.
Join the DZone community and get the full member experience.Join For Free
Software design plays a vital role in the life of a software engineer. It begins with a problem statement and sets the path for implementing a solution. In this article, I’ll be talking about a typical process that I follow to thoroughly understand a problem and successfully deliver a solution.
Defining a problem statement is the very first and crucial phase of the development process. These comes from product, leadership, or another engineers. A well defined problem provides a very clear picture of how the system should look like. But often, How well a problem statement is defined, there are a lot of ambiguities and unknowns. To overcome these, we proceed to requirement gathering.
Once the problem statement is defined, e.g., Develop a chat application, Develop a microblogging site, etc. The very next step is to decode the problem, outline requirements, call out unknowns and dependencies and explain assumptions. Let me explain each section:
1. Decoding problem statement: A single liner problem is a very high level explanation of what the stakeholders end goal. This often is a very abstract statement. In order to design an efficient system, we first need to understand the problem statement by asking various clarifying questions like “who are the users”, “what are the features required”, “what are the constraints” etc. This will provide a good understanding of the ask.
2. Outline requirements: It’s always better to document and take a sign off before implementation. Once the problem statement is clear, the requirements can be broken down into functional and non functional and prioritized to P0, P1, P2, etc., based on timeline, complexity, etc. This will act as a blueprint of what needs to be delivered and by when.
3. Calling out unknowns and dependencies: Once the requirements are final, we are in a shape to outline dependencies and identify unknowns. Often times for a small or large systems, there are certain dependencies on the existing applications or new applications. These dependencies are either from same team or cross team/org, which could be a risk if not resolved on time. These should be outlined in your design document and called out in order to let stakeholders know the risks and resolve these on priority.
4. Assumptions: No system is perfect and there are certain assumption that we need to make either due to: technological limitations, dependency limitations, unknowns or assuming certain existing service will behave in certain way etc. Documenting this will help you during development phase as well as delivery phase.
Back of Envelope Calculations
Back of envelope calculation is very crucial to design your system. This help you in understanding you customers and take decisions on choice of infrastructure, storage, scalability etc. to cater customer need. This will include understanding traffic pattern, volume of data, type of data, storage requirements, etc., which in turn will help you decide in choosing various components for your system.
Once the requirements are precise, we can break these requirements into various tasks or, milestones, or deliverables. This will give readers/stakeholders a clear idea about timelines of the application component deliverables. This will also help us to track the progress as well as reshuffle any priorities. Once deliverables are broken down, it’ll be easier to create stories/tasks and assign these to multiple developers in parallel.
High Level Design
The very crucial and one of the important step: System Design. A high-level design is a blueprint of your application and a go-to guide for developers. Once the requirements are clear, it gives us a very deep understanding of the system. Based on the requirements we identify the various components needed in order to design the system. A high level design composed of these components and interactions between them. It outlines various actors, infrastructure, and interactions among them. To provide a better visibility you could also provide legends, numbering and marking of these interactions. Often times there are not one but many different existing and new components/services which interact together. It’s always better to use color codes or separations to group related components together. This gives reader a clear understanding of interactions between components and their boundaries.
Low Level Design
A high level design should follow a Low Level Design (optional based on complexity). A high level design shows the components and interactions at a very high level while the low level design explains each components in details. This gives a clear visibility about each individual components and will act as a north star for the developers working on those component. You can include as much details like class diagrams, interactions, hierarchy, etc., in this segment
Every software application is a set of commands and steps of execution to achieve desired task. Each executions result in certain set of data flow between various components which dictates how the application will behave. For e.g., call API a and then use that result to derive certain decision and then call API b or Once the API a is invoked, validate authentication and authorization before making any business decision. These can be visualized better in a flow diagram. This will provide a visualization of the flow of data, control, actions, etc.
Once you’ve completed above steps, you will have a design document in a very good shape. The document till now will have enough information to derive the ask, outcome and a path to realize these. But often times one solution doesn’t fit for all and to reach to an optimum solution, you have to consider various different approach. It is a good practice to document all those alternate considerations with pros and cons of each of them. At the end you can conclude your approach with a high level explanation of why did you chose above design. This gives readers a clear picture of your design choice and technical depth.
Once the HLD and LLD are completed, you’ll have a clear understanding of various different components. These components will either host your applications, store data, call downstream or perform some business logic. In this section, you can describe all the data schema and relations used in the design. This will help developers to implement these schema in a clear way and also helps readers understand it.
If the system demands API, then this is the best place to outline each and every API’s. You can write the endpoint details, path details, method type, authentication, authorization, request, and response structure for each API.
This is another important aspect of system design. In this section, you can outline all your design choices and running costs for projected traffic. This will give readers a better understanding of running cost of your system as well any future optimizations that can be done to reduce cost.
Security is not needed unless some incident happens. So it is always better to consider security aspect in your design like Authentication, Authorization, Data Storage, Encryption at Storage, Encryption at REST, Crypto Shredding, Access Policy, Data Classification etc. What will happen if something goes wrong? What will happen if keys are compromised? How to ensure safe storage of the data? etc.
If your design requires UX, you can add a section with mocks, wireframes, etc.
You could write a high level details of how the flow can be tested. Depending on the design, this can be a separate new document to outline testing each flow.
OE plays a very important and vital role in a healthy application. This is a very important pillar of a well-designed and developed application. You can use this section to outline deployment, maintenance, alarm, dashboard, etc., to ensure health of the application.
Last but not least, failure conditions. Every system have certain threshold unless reached. There could be race conditions, corner cases, downstream dependencies, storage layers, etc., which could lead to failure. Outlining these failure conditions and a mechanism to recover from these is always a good practice.
Apart from the above, you could also mention Appendix, Data Modeling, Discussions, etc., in your design document. System design is subjective, and this writing is solely my idea of a good document. Feel free to provide your inputs!
Published at DZone with permission of Pranav Kumar Chaudhary. See the original article here.
Opinions expressed by DZone contributors are their own.