Architecture Decision Record
ADR are short text documents that captures an important architectural decision made along with its context and consequences.
The whole document should be one or two pages long. Written as if it is a conversation with a future developer. This requires good writing style, with full sentences organized into paragraphs. Bullets are acceptable only for visual style, not as an excuse for writing sentence fragments.
- We have a clear log of the different decisions taken, which can help newcomers to understand past decisions.
- It can help in the discussion of such changes.
- Architecture decisions recorded in small modular readable documents.
- More time is required for each change, as we need to document and discuss it.
How to use them⚑
We will keep a collection of architecturally significant decisions, those that affect the structure, non-functional characteristics, dependencies, interfaces or construction techniques.
The documents are stored in the project repository under the
doc/arch directory, with a name convention of
NNN is a monotonically increasing number.
If a decision is reversed, we'll keep the old one around, but mark it as superseded, as it's still relevant to know that it was a decision, but is no longer.
Using Michael Nygard's template as a starting point, I'm going to use these sections:
- Title: A short noun phrase that describes the change. For example, "ADR 1: Deployment on Ruby on Rails 3.0.10".
- Date: Creation date of the document.
- Status: The ADRs go through the following phases:
- Draft: We are using the ADR to build the idea, so everything can change.
- Proposed: We have a solid proposal on the Decision to solve the Context.
- Accepted: We have agreed on the proposal, from now on the document can't be changed!
- Rejected: We have agreed not to solve the Context.
- Deprecated: The Context no longer applies, so the solution is no longer needed.
- Superseded: We have found another ADR that better solves the Context.
- Context: This section describes the situation we're trying to solve, including technological, political, social, and project local aspects. The language in this section is neutral, simply describing facts.
- Proposals: Analysis of the different solutions for the situation defined in the Context.
- Decision: Clear summary of the selected proposal. It is stated in full sentences, with active voice.
- Consequences: Description of the resulting context, after applying the decision. All consequences should be listed here, not just the positive ones.
I'm using the following Ultisnip vim snippet:
snippet adr "ADR" Date: `date +%Y-%m-%d` # Status <!-- What is the status? Draft, Proposed, Accepted, Rejected, Deprecated or Superseded? --> $1 # Context <!-- What is the issue that we're seeing that is motivating this decision or change? --> $0 # Proposals <!-- What are the possible solutions to the problem described in the context --> # Decision <!-- What is the change that we're proposing and/or doing? --> # Consequences <!-- What becomes easier or more difficult to do because of this change? --> endsnippet
Usage in a project⚑
When starting a project, I'll do it by the ADRs, that way you evaluate the problem, structure the idea and leave a record of your initial train of thought.
I found useful to:
Define the general problem at high level in
- Describe the problem you want to solve in the Context.
- Reflect the key points to solve the problem at the start of the Proposals section. Go one by one analyzing possible outcomes trying not to dive deep into details and having at least two proposals for each key point (hard!).
- Build an initial proposal in the Decision section by reviewing that all the Context points have been addressed and summarizing each of the Proposal key points' outcomes.
- Review the positive and negative Consequences for each actor involved with the solution, such as:
- The final user that is going to consume the outcome.
- The middle user that is going to host and maintain the solution.
- Ourselves as developers.
- Use the problem definition of
001and draft the phases of the solution at
- Create another ADR for each of the phases, getting a level closer to the final implementation.
00Xfor the early drafts. Once you give it a number try not to change the file name, or you'll need to manually update the references you make.
As the project starts to grow, the relationships between the ADRs will get more complex, it's useful to create an ADR landing page, where the user can follow the logic between them. MermaidJS can be used to create a nice diagram that shows this information.
In the mkdocs-newsletter I've used the next structure:
graph TD 001[001: High level analysis] 002[002: Initial MkDocs plugin design] 003[003: Selected changes to record] 004[004: Article newsletter structure] 005[005: Article newsletter creation] 001 -- Extended --> 002 002 -- Extended --> 003 002 -- Extended --> 004 002 -- Extended --> 005 003 -- Extended --> 004 004 -- Extended --> 005 click 001 "https://lyz-code.github.io/mkdocs-newsletter/adr/001-initial_approach" _blank click 002 "https://lyz-code.github.io/mkdocs-newsletter/adr/002-initial_plugin_design" _blank click 003 "https://lyz-code.github.io/mkdocs-newsletter/adr/003-select_the_changes_to_record" _blank click 004 "https://lyz-code.github.io/mkdocs-newsletter/adr/004-article_newsletter_structure" _blank click 005 "https://lyz-code.github.io/mkdocs-newsletter/adr/005-create_the_newsletter_articles" _blank 001:::accepted 002:::accepted 003:::accepted 004:::accepted 005:::accepted classDef draft fill:#CDBFEA; classDef proposed fill:#B1CCE8; classDef accepted fill:#B1E8BA; classDef rejected fill:#E8B1B1; classDef deprecated fill:#E8B1B1; classDef superseeded fill:#E8E5B1;
Where we define:
- The nodes with their title.
- The relationship between the ADRs.
- The link to the ADR article so it can be clicked.
- The state of the ADR.
Although adr-tools exist, I feel it's an overkill to create new documents and search on an existing codebase. We are now used to using other tools for the similar purpose, like Vim or grep.