Topic: ADR template for Azure DevOps

ADR Template for Azure DevOps — Repos, Wiki, and Work Items

Azure DevOps gives engineering teams three places to put architecture decision records — Repos (Markdown files in the code repository), Wiki (a separate editable knowledge base), and Work Items (structured metadata). Only one of those locations gives you the immutability guarantee that ADRs require. This page explains which one, how to wire up the ADO Work Item integration for lifecycle tracking, and how to configure Azure Pipelines as the CI validation gate for your decision directory.

TL;DR

Put canonical ADRs in ADO Repos as Markdown files at /docs/decisions/NNNN-title.md, protected by a branch policy that requires PR review before merging. Use ADO Work Items with a custom Architecture Decision Work Item type to track ADR lifecycle status (Proposed → Accepted → Superseded) and link decisions to the Epics or User Stories they govern. Use ADO Wiki for drafting and stakeholder review only — not as the canonical archive, because Wiki pages have no PR gate and can be edited by any Contributor. Add an Azure Pipelines Build Validation policy to enforce ADR structure on pull requests. The ADO export story for Repo files is excellent — git clone gets you everything, already in Markdown, with full history.

The three Azure DevOps surfaces for ADRs

ADO provides three storage surfaces, each with a different fidelity–accessibility trade-off for architecture decision records:

The correct ADO ADR architecture uses all three surfaces in sequence: draft in Wiki, track lifecycle in Work Items, commit the canonical body in Repos. The Repos file is the primary artifact; Wiki and Work Items are the supporting provenance layer.

Setting up ADO Repos for ADRs

The minimal Repos configuration for ADR storage requires choosing a file location, establishing a naming convention, and setting a branch policy that enforces review before ADR changes are merged.

Directory location and naming convention

Place ADR Markdown files in a directory that is co-located with the code they govern, not in a separate documentation repository. The most common conventions by codebase type are:

Use the four-digit-padded numbered filename convention: 0042-use-postgresql-full-text-search.md. Pad to four digits to survive growth past 999 ADRs without renaming. Kebab-case the title. Never change the filename after the ADR is Accepted — the filename is the stable identifier that all cross-references use.

The YAML frontmatter for each ADR file should include the fields that the Azure Pipelines validation script will check:

---
title: "Use PostgreSQL full-text search instead of Elasticsearch"
status: Accepted
date: 2026-05-14
deciders: ["@alice", "@bob"]
supersedes: ""
superseded-by: ""
work-item: "https://dev.azure.com/{org}/{project}/_workitems/edit/2042"
---

## Context

[Two to four sentences. What forced this decision? What constraints exist?
What would happen without this change?]

## Decision

We will use PostgreSQL full-text search instead of introducing an Elasticsearch cluster.

## Consequences

**Positive:**
- No additional infrastructure to operate or monitor.
- Full-text search is co-located with relational queries — a single transaction can combine both.

**Negative (trade-offs):**
- PostgreSQL full-text ranking is less sophisticated than Elasticsearch's BM25 with field boosting.
- Migrating to Elasticsearch later requires a new cluster rollout and a data migration.

**Neutral:**
- The search API surface stays unchanged — the implementation change is internal.

## Alternatives considered

| Option | Why rejected |
|---|---|
| Elasticsearch | Operational overhead disproportionate to search volume at current scale. |
| MeiliSearch | Mature for product search, less proven for technical document retrieval at our query pattern. |

## Deliberation record

Work Item: [ADR-2042](https://dev.azure.com/{org}/{project}/_workitems/edit/2042)
AI chat session: see WhyChose export for the 2026-05-12 Claude session that compared the indexing strategies.

Branch policy for the decisions directory

A branch policy on the main branch ensures that no ADR can be merged to the canonical archive without peer review. To configure this in Azure DevOps:

  1. Navigate to Project Settings → Repos → Repositories → [repo] → Policies, or to Project Settings → Repos → Branches → [main] → Branch Policies.
  2. Under Require a minimum number of reviewers, set minimum reviewers to 1. Enable Allow requestors to approve their own changes to Off for the ADR path (this prevents the author from self-approving without a second review).
  3. Under Build Validation, add the Azure Pipelines ADR validation pipeline (configured below) and set it to Required. Set the path filter to /docs/decisions/*.md so the pipeline only triggers on changes to ADR files — not every commit to main.
  4. Under Automatically included reviewers, add a security group (architecture-reviewers) as an optional reviewer on the /docs/decisions/ path. This notifies the group on every ADR PR without making them a required approver — useful for teams where architecture review is a courtesy copy, not a gate.

Azure Pipelines validation pipeline

The pipeline YAML below validates ADR Markdown files on every pull request that touches the /docs/decisions/ path. It checks the YAML frontmatter fields that define a well-formed ADR, not the prose content — CI can enforce structure but not quality:

trigger: none

pr:
  paths:
    include:
      - docs/decisions/**

pool:
  vmImage: ubuntu-latest

steps:
  - script: |
      pip install python-frontmatter pyyaml
    displayName: Install dependencies

  - script: |
      python - <<'EOF'
      import frontmatter, sys, os, glob

      REQUIRED_FIELDS = ["title", "status", "date", "deciders"]
      VALID_STATUSES  = {"Proposed", "Under Review", "Accepted",
                         "Superseded", "Deprecated"}
      errors = []

      for path in glob.glob("docs/decisions/*.md"):
          post = frontmatter.load(path)
          name = os.path.basename(path)
          for field in REQUIRED_FIELDS:
              if not post.get(field):
                  errors.append(f"{name}: missing required field '{field}'")
          status = post.get("status", "")
          if status not in VALID_STATUSES:
              errors.append(f"{name}: invalid status '{status}'")
          if status == "Superseded":
              sup = post.get("superseded-by", "")
              if not sup:
                  errors.append(f"{name}: status Superseded requires non-empty 'superseded-by'")
              else:
                  target = os.path.join("docs/decisions", sup)
                  if not os.path.isfile(target) and not os.path.isfile(target + ".md"):
                      errors.append(f"{name}: superseded-by '{sup}' does not resolve to a file")

      if errors:
          for e in errors:
              print(f"FAIL: {e}")
          sys.exit(1)
      print(f"OK: {len(list(glob.glob('docs/decisions/*.md')))} ADR files validated")
      EOF
    displayName: Validate ADR frontmatter

This pipeline is the Azure DevOps equivalent of the ADR GitHub Action. Both enforce the same structural invariants — required frontmatter fields, controlled status vocabulary, and the superseded-by file pointer resolution — using the platform's native CI mechanism instead of the other.

Setting up ADO Work Items for ADR lifecycle tracking

Work Items are the right place to store ADR metadata — lifecycle status, the identity of the engineer who made the call, and the links to other Work Items the decision governs. The ADR body text lives in Repos; the ADR lifecycle metadata lives in Work Items.

Creating the Architecture Decision Work Item type

Azure DevOps allows custom Work Item types at the process template level. The steps differ slightly between Agile, Scrum, and CMMI process templates, but the principle is the same across all three:

  1. Navigate to Organization Settings → Boards → Process → [your process] → Work Item Types → New Work Item Type.
  2. Name it Architecture Decision. Select the Epic icon (or a custom icon if your organization uses them) to make it visually distinct in board and backlog views.
  3. Add the custom fields described in the table below.
  4. Apply the process customization to the target project under Organization Settings → Boards → Process → [your process] → Projects.

Custom fields for the Architecture Decision Work Item type

Field name Type Values / notes When to populate
ADR Status Picklist (single) Proposed / Under Review / Accepted / Superseded / Deprecated At creation (Proposed). Keep distinct from the standard State field — ADR Status tracks decision lifecycle, not implementation workflow.
Decider Identity (single) The engineer or EM who made the final call — not the Work Item creator When Status moves to Under Review. Prevents "who owns this?" during handoffs.
Date Decided Date/time When ADR Status moved to Accepted — not the Work Item creation date At graduation. The gap between Created and Date Decided reveals how long decisions take to formalize — a useful team health metric.
Supersedes Integer (Work Item ID) Work Item ID of the ADR this one replaces (e.g., 2038) Only when this ADR replaces a prior decision. Enables querying the supersession chain in Work Item queries.
Repo File URL (or text) Direct URL to the committed Markdown file in the ADO Repos browser Added at graduation. Turns the Work Item into a pointer to the canonical Markdown file — the Work Item becomes the provenance record, the file is the ADR.

Linking Architecture Decision Work Items to implementation work

ADO Work Items support several link types. The most useful for ADRs are:

Name Architecture Decision Work Items using the convention ADR-NNNN: [Title — name the decision, not the problem] — for example, ADR-0042: Use PostgreSQL full-text search instead of Elasticsearch. This makes ADR Work Items scannable in the backlog and searchable by number in PR comments across the entire project. Teams with hundreds of Work Items often set up a dedicated Architecture area path and a dedicated Architecture Decisions board to surface ADR Work Items separately from sprint work.

Using ADO Wiki for drafting

ADO Wiki is useful for drafting ADRs before they are ready for the formal review cycle, and for publishing a human-readable index of accepted decisions for stakeholders who do not navigate the code repository. The key constraint to communicate to your team: the Wiki page is a draft surface, not the canonical ADR.

Setting up the Architecture Decisions section in ADO Wiki

  1. In the ADO project Wiki, create a top-level page called Architecture Decisions. Add a child page called Index that lists all ADRs by number with their status — this is the human-readable register your non-engineer stakeholders will reference.
  2. Create a page template (a page named _ADR-Template or similar) that engineers copy when drafting a new ADR. The template should contain the full Nygard-format structure with guidance comments in each section.
  3. Wire the Wiki index page to the committed Markdown files in Repos: for each Accepted ADR, the Wiki index entry links to the file in the Repos browser (the URL follows the pattern https://dev.azure.com/{org}/{project}/_git/{repo}?path=/docs/decisions/NNNN-title.md). Engineers click through from the Wiki index to the authoritative file.

Because the ADO Wiki is stored as a separate git repository, you can clone it locally: find the clone URL under Wiki → More options → Clone wiki. This means Wiki pages are recoverable via git clone even without the ADO interface — a significantly better export story than Confluence or Notion for teams migrating off ADO.

ADO export — the best story of any PM tool for ADR archival

Understanding what Azure DevOps exports determines your migration risk. ADO's export story is the strongest of any project management tool for ADR storage:

Item Export path Format recovered Notes
ADR Markdown files in Repos git clone — the complete repository with full history Native Markdown, all YAML frontmatter, complete commit log The strongest export story — no API conversion required. Survives any ADO subscription change.
ADO Wiki pages git clone of the wiki repository (separate clone URL in Wiki settings) Markdown files with complete edit history ADO Wiki pages are Markdown files in a git repo — clone and you have the full content. Significantly better than Confluence HTML export.
Architecture Decision Work Items Boards → Work Items → Export to CSV; or REST API at /wit/workitems?ids=… CSV (custom fields included); JSON via REST API Custom fields like ADR Status, Decider, Date Decided are included in both CSV and API responses. Description field is plain text in CSV, HTML in API responses.
Work Item links (Related, Predecessor, Successor) REST API at /wit/workitems/{id}?$expand=relations JSON with rel type and url for each linked Work Item The relational ADR-to-Epic link graph is only fully recoverable via the API. CSV export does not include Work Item link metadata.
Azure Pipelines ADR validation history Build results in ADO Pipelines UI; REST API at /build/builds?definitionId=… Build logs and test results per run The pipeline run history shows which ADR PRs passed or failed validation and why — useful for an ADR quality audit trail.

The git-based export for both Repos and Wiki is what makes ADO a migration-resilient ADR home compared to Jira and Confluence. A Jira export requires ADF-to-Markdown conversion that is lossy for tables and code blocks; an ADO Repos clone recovers the Markdown file unchanged. Teams migrating from ADO to GitHub get their entire ADR archive for free with git clone and git push.

Azure DevOps vs other platforms for ADR storage

Platform ADR body storage Immutability Export path Work Item integration
Azure DevOps Repos Markdown files in code repo, co-located with the code they govern Full — git commit trail, branch policy enforces PR review git clone — native Markdown, full history, no conversion Yes — link ADR Work Items to Epics/Features via Related link type; Build Validation pipeline enforces structure
GitHub Markdown files in code repo, same pattern as ADO Repos Full — git commit trail, branch protection rule enforces PR review git clone — identical to ADO Repos export story Yes — link ADR PR to Issues via closing keywords; GitHub Actions CI for validation (see ADR GitHub Action)
Jira + Confluence Confluence page (ADR body) + Jira Issue (metadata) None — Confluence pages editable without PR; Jira descriptions editable by any project member Confluence Space export (HTML, not native Markdown); Jira CSV (description as plain text, formatting stripped) Yes — Jira Issues macro, Jira Issue Links, bidirectional via Jira Epic ↔ Confluence page
Linear Linear Documents (drafting); Markdown files in repo (canonical) None for Documents — editable by workspace members; full for graduated Markdown files Linear Documents: GraphQL API only (body not Markdown, no CSV/file export); Markdown files: git clone Yes — Relations feature links Documents to Issues and Projects; bidirectional navigation in Linear UI
Notion Notion database page (drafting); Markdown files in repo (canonical) None for Notion pages; full for graduated Markdown files Notion Markdown export per page — closest to native Markdown of the non-git PM tools Via Relation property between ADR database and tasks database; or via @mentions in page body
Obsidian (git-backed vault) Obsidian notes (drafting + canonical, if vault is git-backed) Full if vault is git-backed — same git immutability as ADO Repos Native Markdown files — git clone of the vault repository Via wikilinks and YAML frontmatter fields; Dataview queries for ADR status dashboards

Why this matters for decision capture

The Azure DevOps ADR workflow — Repos for the canonical file, Work Items for lifecycle metadata, Wiki for drafting — solves the storage and tracking problem for decisions that are written down. But it does not solve the upstream gap: decisions that were made in AI chat sessions before anyone opened ADO.

In practice, significant architecture decisions are often worked through in ChatGPT or Claude before the engineer creates a Work Item or writes the first line of an ADR. The trade-off analysis, the alternatives comparison, the constraints that ruled out the second option — that reasoning lives in an AI chat session. The Work Item that follows captures "we decided to use PostgreSQL FTS," but not why Elasticsearch was rejected or what the conversation thread looked like when the team reached consensus.

For decisions already captured in AI chat history, the WhyChose extractor reads your ChatGPT or Claude conversations.json export and surfaces the architecture calls inside as structured decision records — with Context, Decision, and Consequences sections reconstructed from the conversation thread. The output is Nygard-compatible Markdown ready to drop into /docs/decisions/, with the frontmatter fields pre-populated for the Azure Pipelines validation pipeline. Paste the extracted ADR into an Architecture Decision Work Item description during the review cycle, merge it via the branch policy PR, and update the Repo File field to point at the committed file. The ADO Work Item becomes the provenance record; the committed Markdown file is the canonical ADR.

Get early access

Related questions

Should I put ADRs in the ADO Repo or the ADO Wiki?

ADO Repos is the right location for canonical ADRs. Markdown files committed to a code repository carry the immutability guarantee ADRs require: every change is a commit with author, timestamp, and diff; the PR branch policy enforces review before any change is merged; and the file survives project renaming or archiving. ADO Wiki is editable by any Contributor without a PR gate — it is useful for drafting and stakeholder review, but it cannot serve as the authoritative archive. Draft in ADO Wiki, then graduate to a committed Markdown file in /docs/decisions/ via a pull request with the branch policy applied.

What Work Item fields are most useful for ADR tracking in Azure DevOps?

The four most useful custom fields for an Architecture Decision Work Item type are: ADR Status (picklist: Proposed / Under Review / Accepted / Superseded / Deprecated — distinct from the standard State field so it does not disrupt the standard Active/Resolved/Closed workflow), Decider (identity field — the engineer who made the final call, not the Work Item creator), Date Decided (date field — when Status moved to Accepted, not when the Work Item was created), and Repo File (URL field — the path to the committed Markdown file in the Repos browser, added at graduation). A fifth field, Supersedes (integer — the Work Item ID of the ADR this one replaces), is useful for teams with an active supersession cycle. Do not add more than five fields initially — schema inflation makes the Work Item type feel bureaucratic and engineers stop filling in metadata consistently.

How do I set up Azure Pipelines validation for ADR files?

The pipeline YAML is triggered on pull requests that touch the /docs/decisions/ path. It runs a Python script that checks every Markdown file in the directory for required frontmatter fields (title, status, date, deciders), valid status vocabulary (Proposed / Under Review / Accepted / Superseded / Deprecated), and a non-empty superseded-by field whose value resolves to a file in the same directory for any Superseded ADR. The pipeline is added as a required Build Validation in the target branch's Branch Policies under Project Settings → Repos → Branches → [main] → Build Validation. Pull requests that modify ADR files cannot be completed until the pipeline passes.

Does the Azure DevOps export include ADR Markdown files?

Yes — and this is the strongest export story of any PM tool for ADR storage. Markdown files committed to an ADO Repos repository are exported via git clone: the full file content, complete commit history, and all branch and tag metadata are available locally without any API conversion step. The files are already Markdown, already version-controlled, and already immutable. For ADO Wiki, the wiki content is stored as a separate git repository cloneable from the Wiki's clone URL. Work Item data exports via the ADO CSV export or the Azure DevOps REST API (/wit/workitems), with custom fields included in the API response.

Further reading