Documenting Architecture: Architecture Decision Log
The context is the key
I bet that every developer will step into a code that is weird or hard to work with. The most common reaction in such cases is cursing the original authors. However, these curses might not be justified at all. In my career, I have never met any developer who would deliberately create crappy code.
In my opinion, the problem lies somewhere else. When we see someone else’s code that is mediocre, we usually lack context. The code does not tell the reader what constrained the team members, how skilled they were, or what technologies they were forced to use. All this information can change the perception of crappy code entirely.
In the projects, I was involved in, and Architecture Decision Log solved the above problem well. This tool is designed to capture the decisions made by the development team and store them in an immutable list for future maintainers.
Architecture Decision Log
Architecture Decision Log (or ADL for short) is a tool that helps keep track of the decisions related to a software project. It consists of Architecture Decision Records (or ADRs). Each ADR is a simple text file. It covers a single decision. ADL is append-only. In other words, previously added ADRs cannot be altered or deleted. However, this does not mean that once a decision is made in the project, it cannot be changed. When the team members realize that they chose poorly, they can simply add new ADR to invalidate the previous one. The main advantage of such an approach is the fact that old ADRs do not need updates as the project progresses. Also, a full history of the decisions is kept. It allows the team members to “look into the past” and examine their thinking process.
Each ADR consists of several parts. These are:
- Title: a brief summary of the covered decision.
- Status: pending for decisions that were not yet approved by the team, accepted or rejected. It is valuable to keep the rejected decisions to keep track of the solutions that were already considered and dismissed.
- Context: the description of all known circumstances that affect the decision. These could include the current technological stack, requirements, available skills of the team, etc. As a rule of thumb, I would suggest putting as many positions as possible, even if some of them might have just a slight influence on the decision.
- Consequences: all known implications of the decision. This part should contain both positive and negative results. This section is extremely important as it should help the team analyze what cost will each decision have.
- Alternatives: list of possible alternate solutions to the problem. Each considered solution should have a brief description. Also, the reasons for declining each possibility should be pointed. By completing this section, the team will be forced to at least consider other solutions. From my experience, the need to explore other possibilities sometimes leads to the selection of a more suitable option.
- Decision date: the date of acceptance or rejection.
Please note that the construction of an ADR forces the team members to understand the consequences of the choice they are going to make. With a thorough understanding of the implications of each decision, the team members will better understand the design. Furthermore, it will be less likely to encounter unforeseen circumstances that could slow down the development or even lead to severe bugs.
An example of an ADR is presented below. This decision depicts the selection of the blog engine that is used to power this blog.
The blog engine
Status: accepted
Context
- I decided to start a personal blog.
- The blog will be hosted on small VPS to keep the monthly cost low (below $10/month).
- The VPS will run OpenBSD.
- The blog engine should allow creating blog posts with Markdown.
- The blog should be lightweight and fast.
- I will not have access to the deployment pipeline as it would add extra cost that would not be justified as the blog will be non-profit.
Decision
I decided to use Hugo. Hugo is a static site generator written in Go. It allows for rendering static pages from the Markdown source. It is available as a binary package in OpenBSD.
Consequences
The binary package for Hugo should be installed to render the pages on the target server. Furthermore, the blog theme should be chosen or developed. Since I have no previous experience with Hugo, I will need to familiarize myself with this solution and configure the environment. It will take some amount of time, up to several hours. Finally, the web server capable of serving static content should be set up.
Alternatives
Jekyll is the other mature, blog-oriented static site generator. It uses GitHub-flavored Markdown. However, it is written in Ruby and is not available as a package in OpenBSD. As a result, it would require a bit more work regarding installation and maintenance of the site generation tool.
The other approach would be to use a CMS solution like WordPress to host the blog. However, this would require not only the CMS to be installed but also a database solution to store the content. This will significantly increase the resource requirements for serving the blog efficiently.
Decision date: 2021-10-20
What decisions to log?
The big question when using an ADL is what decisions should be logged? From my experience, the ADR should be created for every solution incorporated into the code that might be questionable in the future or might have severe consequences. Examples of such decisions are:
- distribution of responsibilities between system components,
- architecture style used for a component/system,
- libraries or frameworks used to achieve desired results,
- pieces of infrastructure utilized in the system,
- design patterns used to solve a particular problem,
- “dirty hacks” used in the codebase.
Please bear in mind that the above list is not exhaustive. You will probably encounter different situations when an ADR will be desired.
I have one more hint that might be helpful. If you only consider putting an ADR in the project, this is probably a sign to create one. In the future, it would be less harmful to have one ADR more than lacking the ADR for a convoluted piece of code.
Practical notes
I usually store the ADL as a directory of Markdown files in the git repository that contains the component code. As a result, the ADL is auditable and reviewable via pull requests. Furthermore, the decisions lie near the code they affect. If the project spans across several repositories, I would recommend creating a repository for system-wide ADL. This global log would store the decisions that affect the whole system, for instance, components responsibilities or integration patterns used. Local ADL with decisions related to a specific component can be stored in its repository.
However, this scheme is not mandatory. The ADL can be stored in any kind of knowledge base or Wiki. Solutions like Dropbox or Google Drive are also acceptable. There is just one thing to remember. The location of an ADL must be known to all team members. Also, all team members must have access to the ADL.
Comments that include a reference to the decision that affects the nearby piece of code are very beneficial. They speed up the search for the decision that influenced the solution. This approach is efficient especially when the decision is related to a design pattern or a “dirty hack”.
Conclusion
The ADL is extremely useful in non-trivial projects. It allows the team to keep track of decisions. Most importantly, each Architecture Decision Record explains thoroughly the context of the chosen solution. As a result, future maintainers can better understand the authors of the original code. As a side effect of creating the ADRs, the team is also forced to explore both the consequences of their decisions and the possible alternatives. From my experience, such an approach leads to better design. Also, the running system is less likely to break in an unexpected manner. The team members get prepared for possible errors in the design phase.