Design

Documenting Architectural Decisions Within Our Repositories

I recently discovered Michael Nygard's article on the subject of Documenting Architecture Decisions. I immediately became interested in using Architecture Decision Records (ADRs) with my projects.

I will provide a brief ADR summary, but I recommend reading Michael Nygard's article before continuing.

Table of Contents:

  1. An Overview of Architecture Decision Records
  2. Using ADRs in Your Projects
    1. Installation
    2. Initialization
    3. Creating a New ADR
    4. Linking ADRs
    5. Superseding ADRs
    6. Other adr-tools Tricks
      1. Listing ADRs
      2. Generating Summary Documentation
      3. Upgrading the ADR Document Format
  3. Putting it All Together
  4. Further Reading

An Overview of Architecture Decision Records

The motivation for using ADRs comes from a common scenario that all developers become familiar with:

One of the hardest things to track during the life of a project is the motivation behind certain decisions. A new person coming on to a project may be perplexed, baffled, delighted, or infuriated by some past decision. Without understanding the rationale or consequences, this person has only two choices:

1. Blindly accept the decision
2. Blindly change it.

Instead of leaving developers to operate blindly, we should record significant decisions affecting the structure, dependencies, interfaces, techniques, or other aspects of our code.

Rather than maintain a large document which nobody will read, we'll house these decisions within our repositories so they are easily accessible.

The ADR format summarizes decisions in five parts:

  1. Title
  2. Context
  3. Decision
  4. Status (e.g. proposed, accepted, deprecated, superseded)
  5. Consequences (good, bad, neutral)

ADR records should be kept short (maximum of two pages) so they are easily digestible by developers.

One ADR will document one significant decision. If a decision is reversed, amended, deprecated, or clarified, we'll keep the corresponding ADR. We'll generate a new ADR, link the related decisions together, and mark the previous decision with a relevant status note.

By keeping a full history of decisions, we help developers see the evolution of our decisions through time and provide the full context for each decision.

Now that we have a basic understanding of what an ADR is, let's see how we can use them in our projects.

Using ADRs in Your Projects

The free adr-tools project allows you to create and manage architecture decisions directly within your projects. No need to worry about managing yet-another-document in some-other-place-we-can't-remember.

ADRs are numbered in a sequential and monotonic manner (0001, 0002, 0003, …). The records are created as Markdown files so they can be parsed by GitHub and documentation tools.

Installation

adr-tools can be installed by adding the git project or a packaged release to your PATH.

Alternatively, OS X users can install adr-tools with Homebrew:

brew install adr-tools

Initialization

Once adr-tools is installed, you will need to enable support inside of your repository using the adr init command. The command takes an argument which specifies where the ADRs should live. For example:

adr init doc/architecture/decisions

The adr init command will create the first ADR in your repository, which notes that you have decided to record architecture decisions: 0001-record-architecture-decisions.md.

# 1. Record architecture decisions

Date: 2018-03-20

## 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 in this article: 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_ at https://github.com/npryce/adr-tools.

Creating a New ADR

To create a new ADR, use the adr new command:

adr new Title For My Decision

This will create a new decision record in the form of XXX-title-for-my-decision.md.

If the VISUAL or EDITOR environment variables are set, the editor will automatically open the file. Otherwise you will need to manually open the file for editing.

Linking ADRs

You can link two ADRs together using the adr link command:

adr link SOURCE LINK TARGET REVERSE-LINK

The SOURCE and TARGET arguments are references to an ADR, which can be either a number or partial filename. The LINK argument is a description that will be added the SOURCE ADR, and the REVERSE-LINK option is a description that will be added to the TARGET ADR.

For example, here is a link which indicates ADR 12 amends ADR 10:

adr link 12 Amends 10 "Amended by"

You can also link ADRs when creating a new one using the -l argument:

-l TARGET:LINK:REVERSE-LINK

Similarly to the arguments for the adr link command, TARGET references the ADR which we are linking to, LINK is the description that will be added to our new ADR, and REVERSE-LINK is the description which will be added to the TARGET ADR.

To use our amendment example above:

adr new -l "12:Amends:Amended by" Brand New Decision

You can provide multiple -l options when creating a new ADR to enable linking to multiple existing records.

Superseding ADRs

When creating a new ADR, you can indicate that it supersedes an existing adr using the -s argument:

adr new -s 12 Brand New Decision

The status of the superseded ADR (0012 in the example above) will be updated to indicate that it superseded by the new ADR. The newly created ADR will also have a status which indicates the ADR that it is superseding.

You can provide multiple -s options when creating a new ADR.

Other adr-tools Tricks

While creating, linking, and superseding ADRs is primarily how we will interact with adr-tools, other options are available.

Listing ADRs

The adr list command will provide a list of all ADRs in your project:

$ adr list
docs/architecture/decisions/0001-record-architecture-decisions.md
docs/architecture/decisions/0002-remove-simulator-from-project.md
docs/architecture/decisions/0003-meson-build-system.md
docs/architecture/decisions/0004-link-with-whole-archive.md

Generating Summary Documentation

The adr generate command can be used to generate summary documentation. Two options are currently provided: toc and graph.

The toc argument will generate a Markdown-format table of contents:

$ adr generate toc
# Architecture Decision Records

* [1. Record architecture decisions](0001-record-architecture-decisions.md)
* [2. Remove simulator from project](0002-remove-simulator-from-project.md)
* [3. Meson Build System](0003-meson-build-system.md)
* [4. Link With --whole-archive](0004-link-with-whole-archive.md)

The graph argument will generate a visualisation of the links between decision records in Graphviz format. Each node in the graph represents a decision record and is linked to the decision record document.

$ adr generate graph
digraph {
  node [shape=plaintext];
  _1 [label="1. Record architecture decisions"; URL="0001-record-architecture-decisions.html"]
  _2 [label="2. Remove simulator from project"; URL="0002-remove-simulator-from-project.html"]
  _1 -> _2 [style="dotted"];
  _3 [label="3. Meson Build System"; URL="0003-meson-build-system.html"]
  _2 -> _3 [style="dotted"];
  _4 [label="4. Link With --whole-archive"; URL="0004-link-with-whole-archive.html"]
  _3 -> _4 [style="dotted"];
}

The link extension can be overridden with the -e argument. For example, to generate a PDF visualization which links to ADRs with PDF extensions:

adr generate graph -e .pdf | dot -Tpdf > graph.pdf

Upgrading the ADR Document Format

If the ADR format changes in a future adr-tools version, you can upgrade to the latest document format using the adr upgrade-repository command.

Putting it All Together

If you're curious about what ADRs look like in practice, I recommend reviewing the adr-tools decision records.

After trying out adr-tools and documenting my architecture decisions, I'm hooked. As a consultant, I frequently work on a variety of projects and am frustrated by the lack of documentation. I hope to leave other developers with the context for my decisions and prevent that frustration from spreading.

I encourage you to give ADRs a try. Keeping a list of running decisions in a simple and digestible manner is much easier than maintaining large specification documents.

Further Reading

Interrupt Handler Rules of Thumb

I was recently involved in a discussion about suitable operations for interrupt handlers. Interrupts can be an intimidating and/or tricky area of embedded systems development, so I want to share some guidelines with you.

Generally you will want to keep your interrupt handlers as simple as possible. The normal flow of our program has been interrupted, so we need to handle the interruption as quickly as possible and return to normal operational flow (or go back to sleep).

Here are some general rules of thumb for operations to avoid:

  • Don't declare any non-static variables inside the handler
  • Avoid blocking function calls
  • Avoid non-reentrant function calls
  • Avoid any processing that takes non-trivial time
  • Avoid operations with locks as you can deadlock your program in an ISR
  • Avoid operations that involve dynamic memory allocations
    • The implementation may require a lock
    • The allocation typically takes a non-determinate amount of time

Depending on your architecture and operational model, your interrupt handler may utilize the stack of the interrupted thread or a common "interrupt stack". Regardless of the model, I advise you to be parsimonious and to avoid stack allocations inside your interrupt handler.

In most cases, there should be little-to-no processing inside of the handler. If I am using an RTOS on my embedded system, my interrupt handlers set an event flag (or semaphore) and clear the interrupt state. The event flag will be tied to a processing thread which wakes up and handles the event in user space.

These guidelines have their exceptions, of course. If you are not using your RTOS, it is likely that your program flow is either a run loop or entirely interrupt driven. It is highly likely that locks and dynamic memory allocation aren't utilized on those systems, reducing deadlock risk. For both of these cases, you may need to use heavier operations inside of the interrupt handler.

Do you have any useful rules of thumb for interrupt handlers? Leave a comment!

A New Approach to Defining Human Touch Temperature Standards

One of the aspects I enjoy about embedded systems is that many products will be physically handled by your target audience. Knowing that a person is going to use your product informs many aspects of design: your software must be stable, your interfaces must be intuitive, and your hardware must be safe to handle.

Modern devices are being increasingly reduced in size. As size is reduced, thermal constraints become much more important and much harder to dissipate. However, burning people does not bode well for our product, so it's important to set temperature limits where people will touch the product.

Many system designers set their product's skin temperature limits based on "onset of damage" to a person. Often this standard is used to gain as much computational performance as possible: stricter thermal constraints require us to scale back on power.

However, I think that my products should not cause users any potential damage. After some digging, I found out that NASA agrees. NASA has created a new approach for defining touch temperature limits which can be used to estimate safe touch temperatures from one second to infinite durations. NASA now advocates "onset of pain" as the standard they currently use for spacecraft design.

The authors found many strange details with the previous touch temperature standards. For instance, NASA's previous lower temperature limit did not trace to any studies or data, and their upper temperature limits used a single value for all materials. They sought to improve NASA's touch temperature standards and back the recommendations with data. The paper describes an approach for setting temperature limits based on contact time (e.g. 1-10s or infinite), which can be used for determining temperature limits for your own products.

For commonly used materials, 45°C appears to be a safe hot touch temperature limit:

The left hand termini of the lines in the figure correspond to the properties of aluminum. Since none of the lines dip below 45°C (113°F), a material temperature limit of 45°C (113°F) can be used as a first screening point for all commonly used materials. That is, if the temperature of any commonly used material is lower than 45°C (113°F), it will meet the hot touch temperature requirements for all contact times.

For cold temperature limits, 10°C is the recommendation. Lower temperatures quickly lead to onset of pain, numbness, and frostbite.

The tests showed that a hand skin temperature of 10°C (50°F) was tolerable [12], so this was taken as the skin temperature limit.

Our products should not cause users harm. Reconsider your team's touch temperature standards on future products. Let's improve the standard for the devices we plan to put in people's hands.

I've included my highlights from the paper below, but you can also see my annotated copy of the paper if you're interested.

Read NASA's A New Approach to Defining Human Touch Temperature Standards

My Highlights

Starting with a summary of the problem:

Defining touch temperature limits for skin contact with both hot and cold objects is important to prevent pain and skin damage, which may affect task performance or become a safety concern. Pain and skin damage depend on the skin temperature during contact, which depends on the contact thermal conductance, the object's initial temperature, and its material properties. However, previous spacecraft standards have incorrectly defined touch temperature limits in terms of a single object temperature value for all materials, or have provided limited material-specific values which do not cover the gamut of likely designs. A new approach has been developed for updated NASA standards, which defines touch temperature limits in terms of skin temperature at pain onset for bare skin contact with hot and cold objects. The authors have developed an analytical verification method for safe hot and cold object temperatures for contact times from 1 second to infinity.

Touch temperature standards in an important detail to consider in our system design:

Correctly defining touch temperature limits for spacecraft equipment protects the crew from pain and physical harm while optimizing crew and system performance. If the touch temperature limits are set too conservatively, the use of operational constraints, such as wearing gloves, may be mandated unnecessarily— increasing the time required to perform tasks. Alternately, thermal coverings may be mandated for powered equipment that would impede their heat transfer to the cabin — decreasing their reliability by causing them to run hotter than is truly required. If the touch temperature limits are set too liberally, the crew may experience pain or skin damage while handling equipment. Therefore, it is important that spacecraft touch temperature limits be defined correctly.

NASA's original hot touch temperature limits were based on human testing with heated aluminum plates. The data showed that pain onset occurred at an aluminum plate temperature of 45°C (113°F).

This temperature was established as the limit for all materials for any contact time from zero to infinity and was the standard until 1995, when two changes were made:

  1. Material—specific hot temperature limits were established for times between 1 and 10 seconds.
  2. A cold temperature limit of -18°C (0°F) for unlimited contact with all materials was added.

Scary that the previous lower temperature limit does not track to any real data:

NASA's cold touch temperature limit of -18°C (0°F) does not trace to any studies or data, and appears to go beyond anecdotal limits for pain. An updated hot and cold touch temperature standard, backed by data, was needed.

"Onset of damage" is a common way to define temperature limits. However, NASA argues this is not ideal:

When defining touch temperature limits, the question arises as to whether pain or skin damage should be the limiting factor. The danger of allowing skin damage is that it may affect a person's ability to use the affected area, including hands or fingers, which may lead to decreased performance in critical areas such as controlling a vehicle.

NASA agrees that damage is not a good way to define temperature limits:

NASA holds to a higher standard, using the onset of pain as the crew protection limit.

The epidermal/dermal interface was chosen as the critical location because only the dermis is sensitive to pain. The epidermis is insensitive to temperature owing to its lack of pain receptors.

Given that the data on pain converge around the same value, the 44°C (111.2°F) epidermal/dermal interface temperature derived by Hatton and Halfdanarson from Stoll et al.'s data should be used as the upper limit for contact with hot objects. A subtle, but important distinction when defining touch temperature limits is that they should be based on the epidermal/dermal interface temperature at pain onset. Some previous standards have incorrectly defined the allowable object temperature as the used the skin temperature limit, which is overly conservative.

45°C is a safe temperature for common materials:

The left hand termini of the lines in the figure correspond to the properties of aluminum. Since none of the lines dip below 45°C (113°F), a material temperature limit of 45°C (113°F) can be used as a first screening point for all commonly used materials. That is, if the temperature of any commonly used material is lower than 45°C (113°F), it will meet the hot touch temperature requirements for all contact times.

The original NASA cold temperature limit is too low, and damage can actually occur before the cold temperature limit is reached:

The origin of NASA's cold temperature limit of -18°C (0°F) for any material for unlimited contact time is unclear, and upon reviewing the literature it was found that this limit is likely to cause both pain and skin damage. Research on human tolerance to cold has shown that onset of pain occurs at 15°C (59°F) skin temperature (Havenith et al. [10]), numbness occurs at 7°C (44.6°F) (Provins and Morton [11]) and risk of frostbite is risked at 0°C (327) (Havenith et al. [10]).

10°C is the new recommended lower touch temperature limit

The tests showed that a hand skin temperature of 10°C (50°F) was tolerable [12], so this was taken as the skin temperature limit.

The conclusion provides insight into why we care so much about defining temperature standards for the human hand:

These limits pertain primarily to intentional contact with the hands and fingers, as some parts of the body may be slightly more sensitive to pain owing to their thinner epidermis. However, the hands and fingers are the most likely parts of the body to be unprotected and used for grasping and manipulation of objects.