As a software engineer you make architectural decisions all the time. Neal Ford calls these software engineers 'accidental architects'. I personally prefer the term implicit architects because I don’t think software engineers doing architecture is in any way an accident or even something you would not want. You’re the expert after all. Decision making is one thing though, how do you document these decisions?
Introduction
In my current project my function title is Software Architect. I have mixed feelings about this. While it definitely looks good on a resume I actually don’t feel software architect is a function title. Software architect is in my opinion a role, and one that can’t be handled by someone that doesn’t have his or her boots on the ground where the software development happens.
This is reflected in many teams where there is an 'architect' that, every now and then, comes down from his golden throne in his lofty ivory tower to enlighten the code monkeys software engineers with his architecture. After enlightening the engineers he again ascends the thousand stairs to his throne to work on his next perfect work.
Another word for this is a seagull architecture; architects that, like seagulls, fly down cover everything with shit and then fly off again. Leaving the software engineers wondering what they did wrong in a previous life to deserve all this. And if you’re lucky you don’t have a single seagull to deal with, but a whole team of them with conflicting ideas on the direction your teams should take.
We Need Architecture
Unfortunately we can’t do without architecture. Most of us don’t work in isolation. The larger the piece of software you are working on the more 'stuff' is happening outside your field of view that still impacts your work. So what is architecture? A nice quote:
Architecture is the decisions that you wish you could get right early in a project
That’s what software architecture boils down to; trying to predict the future as best as you can for the stuff that matters; the things that are hard to change later. And who can do that better than the experienced engineers who will have to deal with the fallout of making the wrong decision? Engineers; yes. A single 'architect' working in isolation won’t work. Even when you have one person with the explicit architecture role, you still need to have buy-in from the other engineers. It’s nice that you as an architect have a brilliant idea but if the rest of the engineers aren’t buying it, it’s not going to happen.
This is what you see with most Ivory Tower architects; their ideas simply get ignored and worked around.
So if we have determined that we need architecture and who is responsible for it, how are we going to approach it?
Architecture Decision Records
So what we do as architects is, as a group that straddles the boundaries between teams, we make decisions that in the long run benefit the organisation and the people working in it. Those decisions need to be recorded somewhere. Some of them, the cross cutting ones that affect all teams, fit well into a software guidebook. Others only affect the individual applications.
What matters most is that they are stored in an easy to read, easy to write manner in a location near where they are used. You can write stuff in powerpoint and then upload it to confluence (confluence is where knowledge goes to die), but no one is going to read it. A software guidebook, or architecture guidebook, is a living document that changes over time. Part of this document are the rules decided upon, which we record in their own special place.
So, in my opinion, the best way to do this is to have one simple text document per decision in the repository it applies to. Whether it’s just text, Markdown or Asciidoc (I prefer the latter) does not really matter as long as you follow a consistent pattern.
ADR Tools
While the concept by itself is easy enough to use, there’s also some tooling that can make it even easier! I recently started using ADR Tools, a simple command line tool that can create these ADRs for me in a consistent manner. And what’s especially nice is that it can mark outdated ADRs for you as well.
Getting started
So first we install it. I’m on a Mac so I can simply use brew install adr-tools
. I make sure I’m in the root of the project I’m working on and get started by running adr init
. This will initialise the project by creating a doc/adr directory (you can override this) with the first ADR explaining that we will be recording ADRs in this repo. That ADR looks like this:
# 1. Record architecture decisions
Date: 2018-10-05
## Status
Accepted
## Context
We need to record the architectural decisions made on this project.
## Decision
We will use Architecture Decision Records, as [described by Michael Nygard](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions).
## Consequences
See Michael Nygard's article, linked above. For a lightweight ADR toolset, see Nat Pryce's [adr-tools](https://github.com/npryce/adr-tools).
We can change this to whatever we want of course!
Add a new record
So let’s now add something. Let’s say we want to record a decision to use JUnit 4.12 for testing:
$ adr new Use JUnit 4.12 for testing
This will create our second record, /doc/adr/0002-use-junit-4-12-for-testing.md:
# 2. Use JUnit 4.12 for testing
Date: 2018-10-05
## Status
Accepted
## Context
The issue motivating this decision, and any context that influences or constrains the decision.
## Decision
The change that we're proposing or have agreed to implement.
## Consequences
What becomes easier or more difficult to do and any risks introduced by the change that will need to be mitigated.
Most of the content is just placeholders. In this simple document we would document our reasoning for adopting JUnit 4.12.
Supersede a record
But what do we do if we later figure out we made a mistake, or come to a different conclusion. Do we delete a record? No, definitely not. Keep in mind that it’s often important to go back to records to understand why certain decisions were made. We need to keep this history to make sure we don’t make the same mistakes again.
But of course there’s also simpler decisions. So 'recently' JUNit 5 came out. While I would definitely not recommend logging every minor version update of every library in an ADR, major version updates do generally break stuff and as such make these decisions ones that affect things on the long term. So, let’s record that we are now going to use JUnit 5 instead of 4.12:
$ adr new -s 2 Use JUnit 5 for testing
This also created a new ADR but the -s 2 is an option where you mark that this new one supersedes record number 2. What’s nice is that ADR tools will automatically mark number 2 as superseded, number 3 as an ADR that supersedes 2, and link them both together. So after running this command /doc/adr/0003-use-junit-5-for-testing.md is created:
# 3. Use JUnit 5 for testing
Date: 2018-10-05
## Status
Accepted
Supercedes [2. Use JUnit 4.12 for testing](0002-use-junit-4-12-for-testing.md)
[...]
## Status
Superceded by [3. Use JUnit 5 for testing](0003-use-junit-5-for-testing.md)
[...]
If you check out the records on the master branch you will see the 'end' state of the three records and how I also recorded the context and consequences of each decision.
Conclusion
Many of the decisions you make as a software engineer have a long term effect. These decisions, the ones you want to get right as much as possible because they can be hard to change afterwards, are what architecture is about. Keeping this all 'in your head' does not scale beyond a very small team and documenting these on a Wiki like confluence is as frustrating as futile. So why not keep these records, little pieces of documentation, with the source they are documenting? This has the additional benefit of them following the exact same history in your VCS as the rest of your code.
Where you keep the overall decisions that apply to every single project is up to you. I’m personally a fan of keeping a Software Guidebook where these decisions are recorded as well. Another approach is to keep track of your own tech radar on what technology to adopt or avoid. All three of these approaches, architectural decision records, a software guidebook and a technology radar, integrate together really well.