Topic: ADR template for Backstage

ADR Template for Backstage — TechDocs Integration, MADR Format, and Catalog Annotations

Backstage is the CNCF-graduated open platform for building developer portals — it's where engineering organizations surface their service catalog, TechDocs documentation, and increasingly, their architecture decision records. The backstage-plugin-adr community plugin renders ADR Markdown files directly on entity pages in Backstage, turning each service's git-stored decisions into a browsable, searchable list alongside its TechDocs. This page explains the MADR template format the plugin expects, how to wire the catalog-info.yaml annotation, and how to fill the hardest ADR section — Considered Options — from the AI chat sessions where you actually deliberated the decision.

TL;DR

Store ADRs as MADR-formatted Markdown files in docs/adr/ inside your component repository. Add backstage.io/adr-location: docs/adr to catalog-info.yaml. The backstage-plugin-adr renders a status-indexed list on the entity's Backstage page. For cross-cutting decisions, use a separate architecture-decisions repository registered as its own Backstage entity. TechDocs renders the full ADR content; the ADR plugin provides the index view. The Considered Options section — the hardest to write — can be pre-filled from ChatGPT or Claude exports via the WhyChose extractor.

Backstage and architecture decision records

Backstage (backstage.io) is a CNCF-graduated developer portal framework open-sourced by Spotify in 2020. It provides three core features relevant to ADR practice:

The result: with a two-line annotation change in catalog-info.yaml and the plugin installed in your Backstage instance, every service in the catalog surfaces its architecture decision history directly on the entity page. No separate portal, no separate search, no navigation away from the service context where the decision is relevant.

Installing and configuring the backstage-plugin-adr

The ADR plugin ships in two packages from the backstage/community-plugins repository:

Backend setup

In your backend package, add the backend module:

// packages/backend/src/index.ts
backend.add(import('@backstage-community/plugin-adr-backend'));

The backend module handles fetching ADR files from the SCM integrations (GitHub, GitLab, Azure DevOps, Bitbucket) already configured in your Backstage instance. No additional credentials are required beyond what your TechDocs integration uses.

Frontend setup

Add the EntityAdrContent component to the entity page layout in your app. In the component entity page (typically in packages/app/src/components/catalog/EntityPage.tsx), add the ADR tab between TechDocs and any other content tabs:

import { EntityAdrContent, isAdrAvailable } from '@backstage-community/plugin-adr';

// Inside the EntityLayout for components:
<EntityLayout.Route
  path="/adrs"
  title="ADRs"
  if={isAdrAvailable}
>
  <EntityAdrContent />
</EntityLayout.Route>

The isAdrAvailable guard hides the ADRs tab for entities that don't have the backstage.io/adr-location annotation — the tab only appears when relevant.

catalog-info.yaml annotation

Add the ADR location annotation to the entity's catalog-info.yaml descriptor file in the component repository:

apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: payments-service
  description: Async payments processing service
  annotations:
    backstage.io/techdocs-ref: dir:.
    backstage.io/adr-location: docs/adr
spec:
  type: service
  lifecycle: production
  owner: payments-team

The backstage.io/adr-location value is the path to the directory containing ADR Markdown files, relative to the repository root. Common conventions:

Cross-cutting ADRs in a separate repository

For platform-level or cross-service decisions, register a separate architecture repository as a Backstage entity and point the annotation at it:

annotations:
  backstage.io/adr-location: url:https://github.com/my-org/architecture-decisions/tree/main/platform

The full URL form allows the ADR plugin to fetch files from any repository in your SCM integration, not just the entity's own repository. This is the correct approach for a dedicated architecture-decisions monorepo that serves as the organization's ADR library.

The MADR template for Backstage ADRs

The backstage-plugin-adr parses MADR-formatted files most reliably. MADR (Markdown Any Decision Records, developed by Oliver Kopp) uses a consistent frontmatter or heading-level structure that the plugin's index view reads for status, date, and title. The recommended template:

# [Short title — name the decision, not the problem]

* Status: [proposed | rejected | accepted | deprecated | superseded by [ADR-0005](0005-example.md)]
* Deciders: [@alice, @bob, @carol]
* Date: 2026-06-04
* Tags: [payments, infrastructure, security]

Technical Story: [ticket URL or one-sentence description of the triggering event]

---

## Context and Problem Statement

[What situation, constraint, or need led to this decision point? Write for an engineer
who was not in any of the related discussions. Two to four sentences is sufficient.]

## Decision Drivers

* [force 1 — a requirement, constraint, or quality attribute that shaped the decision]
* [force 2]
* [force 3]

## Considered Options

* [Option A — e.g., PostgreSQL]
* [Option B — e.g., MongoDB]
* [Option C — e.g., keep the current SQLite setup]

## Decision Outcome

Chosen option: "[Option A]", because [one-sentence justification connecting the choice
to the Decision Drivers above].

### Positive Consequences

* [what this makes easier — required; an ADR with only upsides is a press release]

### Negative Consequences

* [what this makes harder or what new obligations this creates — at least one required]

---

## Pros and Cons of the Options

### Option A — [name]

[Brief description]

* Good, because [argument a]
* Good, because [argument b]
* Bad, because [argument c — be honest]

### Option B — [name]

[Brief description]

* Good, because [argument d]
* Bad, because [argument e]
* Bad, because [argument f]

### Option C — [name]

[Brief description]

* Good, because [argument g]
* Bad, because [argument h]

Filename convention

Use zero-padded sequential numbers: 0001-use-postgres-for-main-database.md, 0043-switch-payments-to-async-queue.md. The leading number gives the ADR plugin and TechDocs a consistent sort order independent of file modification dates. Use kebab-case for the slug — it becomes the URL in TechDocs and should be readable without decoding. Avoid embedding the decision outcome in the filename (use 0043-payments-queue-architecture.md, not 0043-chose-kafka-for-payments.md) — the filename needs to remain accurate even if the decision is superseded.

Status values the plugin recognizes

The backstage-plugin-adr UI renders colored status badges based on the Status field value. The recognized values and their badges:

Status value Badge color Meaning
proposed Yellow Under review — review window is open
accepted Green Review closed, decision stands
rejected Red Decision was formally rejected — useful to preserve reasoning for future reviewers
deprecated Gray Context became void; no replacement decision exists
superseded by ADR-XXXX Gray (with link) A newer ADR replaced this one; reference required

The plugin is case-insensitive for status matching — Accepted, accepted, and ACCEPTED are all recognized. Use lowercase for consistency with MADR tooling.

TechDocs configuration for ADRs

To render ADR files as full TechDocs documentation pages (beyond the plugin's list view), include them in the mkdocs.yml navigation:

site_name: Payments Service Documentation
docs_dir: docs
nav:
  - Home: index.md
  - Architecture Decisions:
      - Overview: adr/index.md
      - "0043 — Payments queue architecture": adr/0043-payments-queue-architecture.md
      - "0042 — Database selection": adr/0042-database-selection.md
  - API Reference: api.md
  - Runbooks: runbooks/index.md

Auto-generating the ADR nav

Manually maintaining the nav for each new ADR is tedious. Two approaches to automate it:

For most organizations, the awesome-pages approach is simpler and sufficient. The CI-generated nav is worth the investment only when ADR volume is high enough (50+ ADRs in a single repo) that manual nav updates become a real friction point.

Backstage ADRs vs a standalone git repository — when to use each

Criterion ADRs in Backstage (TechDocs + plugin) Standalone git ADR repository
Discovery surface Inline on the entity page — engineers find ADRs where they already look up service information Separate repository — requires knowing where to look
Cross-service browsing Must navigate to each entity individually; no cross-service ADR search out of the box A single grep across the repository finds all ADRs mentioning a component or decision type
CI validation Available in the component's own pipeline (same as code); can validate ADR format as part of the PR check Dedicated pipeline per ADR repository; separation of concerns between ADR validation and code validation
Non-technical reviewer access Backstage requires SSO access — standard for engineering orgs, may exclude non-engineering stakeholders A GitHub repository is accessible to any GitHub user; public repositories are accessible without authentication
Decision scope fit Best for service-scoped decisions (the decision is primarily relevant to engineers working on that service) Best for cross-cutting platform or organizational decisions
History and blame Full git history in the component repo; blame shows who updated the ADR status Full git history in the dedicated repo; same capability, separate namespace

The typical Backstage org ends up with both: service-scoped ADRs in each component repository (surfaced via the plugin), and platform-level ADRs in a central architecture-decisions repository registered as a Backstage entity of kind System or Resource. The plugin annotation on the central entity points at the cross-cutting ADR directory.

ADR governance in Backstage organizations

Most Backstage organizations use GitHub Pull Requests as the ADR review surface — not Teams channels or Jira tickets. The PR-based ceremony for a Backstage ADR:

  1. Create a branch: adr/0043-payments-queue-architecture.
  2. Add the MADR file at docs/adr/0043-payments-queue-architecture.md with Status: proposed.
  3. Open a PR against main. The PR description links to the ADR file. @mention reviewers from dependent services and the platform team.
  4. CI runs: format validation checks that all required MADR fields are present, status vocabulary is valid, and any superseded by reference resolves to an existing ADR file.
  5. At the agreed review deadline, if no blocking review comment, merge the PR and update Status to accepted in the same commit.
  6. Backstage TechDocs regenerates on the next build cycle (typically triggered by the main branch push); the ADR plugin refreshes the entity's ADR list.

CI format validation with GitHub Actions

A minimal format check runs in under 30 seconds and catches the most common ADR quality failures:

# .github/workflows/adr-lint.yml
name: ADR format check
on: [pull_request]
jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Check required MADR fields
        run: |
          for f in docs/adr/*.md; do
            [ "$f" = "docs/adr/index.md" ] && continue
            grep -q "^\* Status:" "$f" || { echo "Missing Status in $f"; exit 1; }
            grep -q "^\* Deciders:" "$f" || { echo "Missing Deciders in $f"; exit 1; }
            grep -q "^\* Date:" "$f" || { echo "Missing Date in $f"; exit 1; }
            grep -q "## Considered Options" "$f" || { echo "Missing Considered Options in $f"; exit 1; }
          done

This check prevents the most common ADR quality regression in Backstage organizations: ADRs merged with empty Considered Options sections, which makes the decision appear to be the only option considered and removes the future-engineer context the section is designed to preserve. See the ADR best practices guide for the full rationale on why Considered Options is the section that matters most for long-term decision value.

WhyChose and Backstage ADR practice

The hardest section to write in any MADR-formatted ADR — and the one most often left thin or empty — is Considered Options and its sub-section Pros and Cons of the Options. Engineers writing ADRs in Backstage organizations face the same problem every ADR practitioner faces: the deliberation happened in Claude or ChatGPT sessions, the options were weighed there, the rejection reasons were articulated there. By the time the MADR file is being drafted in a PR, the reasoning is in a chat history that isn't open in the same window as the ADR template.

The WhyChose extractor processes a ChatGPT or Claude export (conversations.json) and surfaces the sessions where architecture alternatives were evaluated. The extracted output includes the Options Considered and the rejection reasons for each non-chosen option — exactly the content that belongs in the MADR Considered Options section. Copy that output into the ADR file before opening the Backstage PR and the hardest section is done.

The workflow that works well in Backstage orgs: deliberate in Claude or ChatGPT (the tools with exportable conversation history), export, run the extractor to surface the decision session, draft the MADR file with the Considered Options section filled from the extractor output, open the PR. The extractor fills the retrospective reasoning; the PR process handles the governance. The result is a Backstage ADR that reads as if you wrote the Considered Options at decision time — because you did, in the AI session.

Further Reading

The Considered Options section is where Backstage ADRs fail

A Backstage ADR with an empty Considered Options section is a press release, not a decision record. The deliberation happened in Claude and ChatGPT — where the alternatives were named, weighed, and rejected with specific reasons. The WhyChose extractor surfaces those sessions and produces structured decision records with the Considered Options section filled. Copy the output into the MADR file before opening the PR.

Run the open-source extractor   Get hosted access