Architecture documents for firmware usually end up in a Word file. It lives on a shared drive, away from the code. Nobody can see what changed between two versions, and the diagrams are pasted images that someone has to redraw by hand. After a year, the document describes a system that no longer exists.

Docs-as-code fixes that. The architecture is written as plain text, sits in the same Git repository as the firmware, and changes in the same pull request as the code it describes. For documents with real structure (numbered chapters, cross-references, diagrams, a PDF for sign-off), AsciiDoc is the format I use.

In this article I set up the toolchain step by step, show how an arc42 document is split into files, and explain what you get in review that a Word file can never give you.

The whole toolchain on one page: what each part does, what it needs, and its licence.

1. AsciiDoc is the language, Asciidoctor is the tool

The mental model: Markdown’s bigger sibling, built for long documents.

AsciiDoc is a plain-text markup language. Like Markdown, it reads well as source. Unlike Markdown, it has the structure a long technical document needs built in: numbered sections, an automatic table of contents, cross-references, includes, admonitions (NOTE, TIP, WARNING) and diagram blocks.

Asciidoctor is the open-source processor that turns .adoc files into HTML5, PDF, DocBook and other formats. The reference version runs on Ruby; there are also JavaScript (Asciidoctor.js) and JVM (AsciidoctorJ) builds.

NeedMarkdownAsciiDoc
Table of contents and section numbersneeds extra toolingbuilt in (:toc:, :sectnums:)
Cross-referencesmanual anchorsbuilt in (<<section-id>>)
Split a document into filesnoinclude::chapter.adoc[]
Admonitions (NOTE, WARNING)nobuilt in
Diagrams from textdepends on the rendererasciidoctor-diagram
PDF outputexternal toolingasciidoctor-pdf

For a README, Markdown is fine. For a document with twelve chapters and a sign-off, it is not.

2. Install the toolchain

Four pieces: Ruby, Asciidoctor with two extensions, Java for PlantUML, and an editor preview.

Ruby. On Windows, use RubyInstaller and pick a “Ruby+Devkit” build. Keep “Add Ruby to PATH” checked, and at the end let it run ridk install with the MSYS2 development toolchain. On macOS and Linux, use the package manager:

bash
brew install ruby              # macOS (Homebrew)
sudo apt-get install ruby-full # Debian / Ubuntu

Asciidoctor and its extensions:

bash
gem install asciidoctor          # the processor, HTML output
gem install asciidoctor-pdf      # PDF output
gem install asciidoctor-diagram  # diagram blocks (PlantUML and others)
asciidoctor --version

Java, for PlantUML. asciidoctor-diagram calls PlantUML to render diagram blocks, and PlantUML runs on Java. Install a current JDK or JRE (for example from Adoptium) and check java -version. Some PlantUML diagram types also need Graphviz; the PlantUML documentation lists which.

Editor preview. In VS Code, install the “AsciiDoc” extension published by asciidoctor. Open an .adoc file and open the preview beside it (Ctrl+Shift+V).

Check where your diagrams are rendered

Some AsciiDoc tools, including the VS Code extension, can render diagrams through Kroki, a web service. With the public Kroki server, your diagram source leaves your machine. For confidential architecture, keep that option off or run your own Kroki instance.

3. Write and build a first document

Create architecture.adoc:

asciidoc
= Software Architecture
Your Name <you@example.com>
:toc: left
:sectnums:
:icons: font

== Introduction and Goals

The firmware runs on a Cortex-M7 microcontroller.

TIP: Admonitions like TIP, NOTE and WARNING render as styled callouts.

== Building Block View

[plantuml, building-blocks, svg]
----
@startuml
component Application
component Comms
component Storage
Application --> Comms
Application --> Storage
@enduml
----

Build it:

bash
asciidoctor -r asciidoctor-diagram architecture.adoc      # architecture.html
asciidoctor-pdf -r asciidoctor-diagram architecture.adoc  # architecture.pdf

The -r asciidoctor-diagram option loads the extension. On each build it renders the PlantUML block to an SVG and places it in the document. You never draw a box by hand.

4. Structure it as arc42

The mental model: arc42 tells you what to write; AsciiDoc tells you how to split it.

arc42 is a widely used template for software architecture documentation. It has twelve chapters, from goals and constraints to risks and a glossary, and it is published in an AsciiDoc version.

Give each chapter its own file and pull them together in one master file with include::. A change to the runtime view then touches one small file, and the pull request stays readable.

One master file, one file per chapter. The teal chapters are where most diagrams live.

For firmware, a few chapters do most of the work:

  • Building block view: the static structure: components, tasks or active objects, and their interfaces.
  • Runtime view: the important scenarios over time, as sequence diagrams: start-up, an update, a fault.
  • Deployment view: which software runs on which board, MCU and memory.
  • Architecture decisions: why you chose what you chose. The reason is the part that gets lost first.

Start small

You do not have to fill all twelve chapters on day one. Start with context, building blocks and decisions, and leave the other files as headings. An honest short document beats a complete-looking empty one.

5. Review it like code

This is the real payoff. Because the document and its diagrams are text, a change shows up in the pull request as a diff, line by line.

The same architecture change, as a reviewer sees it.

Two habits make it stick:

  1. Change the architecture in the same pull request as the code. If a new component appears in the code, it appears in the building block view in the same review.
  2. Build the documents in CI. Run the same two asciidoctor commands on every push, so a broken include or diagram fails the build, and the latest HTML and PDF are always available.

Commit the source, not the output

Keep the .adoc files and the diagram text in Git. Treat the generated HTML, PDF and diagram images as build output, the same way you treat a firmware binary.

6. Licences

Everything in the core toolchain is free and open source. Check the terms yourself before you rely on them, but this is the picture:

ComponentLicence
AsciiDoc (the language)open specification, developed at the Eclipse Foundation
Asciidoctor, asciidoctor-pdf, asciidoctor-diagramMIT
VS Code AsciiDoc extensionMIT
PlantUMLavailable under several licences, including GPL and LGPL
arc42 templateCreative Commons

MIT is permissive: you can use the tools in commercial and closed-source work. PlantUML runs as a separate program that produces images; its website explains the terms for those images.

Summary

  1. Write architecture as text, next to the code. AsciiDoc gives long documents the structure Markdown lacks, and Asciidoctor turns one source into HTML and PDF.
  2. Use arc42 for the structure. One file per chapter, one master file with include::, and start with the chapters that carry the most weight.
  3. Review it like code. Diagrams as text, changes in the same pull request as the code, and a CI build that keeps the output current.

Sources

  • Asciidoctor documentation: asciidoctor, asciidoctor-pdf and asciidoctor-diagram.
  • arc42 documentation and templates (arc42.org).
  • PlantUML documentation (plantuml.com).
  • AsciiDoc language specification work at the Eclipse Foundation (AsciiDoc Working Group).
  • Kroki documentation (kroki.io).

💬 Questions, corrections, or your own war story? Leave a comment below.