ADR(Architecture Decision Record、アーキテクチャ決定記録)とは、システムの構成に関わる重要な判断を「背景・選択肢・決定・結果」の形で1件1ファイルに残す短い文書です。 「なぜこの DB を選んだのか」「なぜこの認証方式なのか」を、後から加わったメンバーや半年後の自分が読み返せるようにすることが目的です。

この記事では、Nygard 形式・MADR 4.0・簡易版の3種類のテンプレートをコピペ用に掲載し、DB 選定、認証方式、ログ基盤、フロントエンドの状態管理、API のバージョニングの5つの記入例を完成形で紹介します。さらに「その判断を ADR に書くか」の判断基準表、ステータスの運用、docs/adr/ に連番で ADR を作る Python スクリプト(実行結果つき)、プルリクエストで ADR をレビューする流れ、よくあるつまずきと対策までまとめています。

ADR の早見表

ADR は「1つの判断を、1ファイルに、短く」書くのが基本です。 まずはこの表の内容を押さえれば書き始められます。

項目 内容
正式名称 Architecture Decision Record(アーキテクチャ決定記録)
書く対象 後から変えにくい、構成や品質に影響する技術的な判断
1ファイルの単位 判断1つにつき1ファイル(例:docs/adr/0002-database.md)
置き場所 コードと同じリポジトリの docs/adr/ や docs/decisions/
番号 4桁の連番(0001、0002…)。欠番も再利用しない
必須の項目 タイトル、ステータス、背景(Context)、決定(Decision)、結果(Consequences)
あると良い項目 検討した選択肢、判断の基準、決定者、日付、確認方法
ステータス 提案 → 承認 / 却下。後に別の ADR で置き換え済み、または廃止
承認済みの ADR を変えるとき 書き換えず、新しい ADR を作って古い方を「置き換え済み」にする
長さの目安 1〜2ページ。読み終えるのに数分
代表的な形式 Nygard 形式(2011年の提案)、MADR(最新は 4.0.0)
レビュー方法 プルリクエストで提案し、マージで承認とする運用が一般的

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「ADRの書き方|テンプレートと記入例5選・書くべき判断基準」

ADR と Design Doc・議事録・設計書の違い

ADR は「何を作るか」ではなく「なぜそう決めたか」を残す文書です。 似た文書との違いを整理すると、役割が重ならずに済みます。

文書 目的 単位・長さ 書くタイミング 更新のしかた
ADR 判断の理由を残す 判断1つ、1〜2ページ 判断の直前〜直後 承認後は書き換えず、新しい ADR で置き換える
Design Doc 作るものの設計を提案し合意する 機能・システム単位、数ページ以上 実装の前 議論の間は更新し、実装後は参照用
議事録 会議で話したことを残す 会議1回分 会議の直後 原則更新しない
基本設計書・詳細設計書 今のシステムの姿を説明する システム全体 設計工程と変更のたび 最新の状態に合わせて更新し続ける

Design Doc の中で行われた大きな判断を、ADR として切り出して残すと、Design Doc が古くなっても判断の理由は残ります。議事録で決まったことが構成に関わるなら、議事録から ADR へリンクします。設計書は「今どうなっているか」、ADR は「なぜそうなったか」と分けると迷いません。

ADR に書くかどうかの判断基準

「後から変えるのにコストがかかるか」「別の選択肢を検討したか」の2つが当てはまれば ADR に書きます。 迷う判断は次の表で確認します。

判断の例 ADR に書くか 理由
DB の種類・製品を決める 書く 移行に大きなコストがかかる
認証・認可の方式を決める 書く セキュリティと全画面・全 API に影響する
言語・フレームワークを決める 書く 採用や学習コストまで影響する
API のバージョニングやエラー形式を決める 書く 利用者側の実装に影響する
モノリスかサービス分割かを決める 書く 構成全体に影響する
ログ・監視の基盤を決める 書く 運用と費用に長く影響する
チーム共通のコーディング規約を決める 規約ファイルで十分 判断理由が短いなら規約本体に書く
ライブラリのマイナーバージョンを上げる 書かない 変更が小さく、すぐ戻せる
変数名・関数名の付け方 書かない レビューで解決できる
一時的な回避策(次のリリースで直す) 書かない(Issue に書く) 長く残る判断ではない
「あえてやらない」と決めたこと 書く 「なぜやらないのか」が繰り返し質問されやすい
外部サービスの契約・費用の判断 書く(技術面のみ) 費用の詳細は別の資料にし、技術的な理由を残す

判断に迷ったら、短い簡易版で書いておくのがおすすめです。書いて困ることは少なく、書かなかったことで同じ議論を繰り返すほうが時間がかかります。

ADR のテンプレート3種(コピペ用)

チームで1つの形式に統一すると、読む側が迷いません。 最初は簡易版か Nygard 形式で始め、選択肢の比較を丁寧に残したくなったら MADR に移るとスムーズです。

形式 特徴 向いている場面
Nygard 形式 5項目だけのいちばん短い形 まず始めたい、判断の理由を短く残したい
MADR 4.0 選択肢ごとの良い点・悪い点、判断基準、確認方法まで書ける 複数の選択肢を比べた判断、関係者が多い判断
簡易版 日本語見出しで、必要な項目だけ 日本語で運用したい、書くハードルを下げたい

テンプレート1:Nygard 形式

Michael Nygard 氏が 2011 年のブログ記事「Documenting Architecture Decisions」で提案した、ADR の原型となる形式です。

# 1. タイトル(決めたことを短く)

## Status

Proposed(提案) / Accepted(承認) / Rejected(却下) / Deprecated(廃止) / Superseded by ADR-0005(置き換え済み)

## Context

この決定が必要になった背景。技術的・組織的な制約、解決したい課題を事実として書く。

## Decision

私たちは〜することにした。(能動態で、決めたことをはっきり書く)

## Consequences

この決定によって起きること。良いことも、トレードオフや新たに必要になる作業も書く。

テンプレート2:MADR 4.0

MADR(Markdown Architectural Decision Records)は、ADR を Markdown で書くためのテンプレートです。GitHub の adr/madr リポジトリで公開されており、2026年10月時点の最新は 4.0.0 です。公式テンプレートは英語なので、見出しは公式のまま残し、説明を日本語にしたものを載せます。

---
status: "{proposed | rejected | accepted | deprecated | … | superseded by ADR-0123}"
date: {YYYY-MM-DD 最後に更新した日}
decision-makers: {決定に関わった人}
consulted: {意見を聞いた人(専門家など。双方向のやりとり)}
informed: {進み具合を共有した人(一方向の共有)}
---

# {解決した課題と選んだ解決策がわかる短いタイトル}

## Context and Problem Statement

{背景と課題を2〜3文で。問いの形で書いてもよい。関連する Issue へのリンク}

## Decision Drivers

* {判断の基準1。例:運用の手間、性能、費用}
* {判断の基準2}

## Considered Options

* {選択肢1}
* {選択肢2}
* {選択肢3}

## Decision Outcome

Chosen option: "{選択肢1}", because {理由。例:必須の基準を満たす唯一の選択肢だから/判断基準で最も評価が高かったから}.

### Consequences

* Good, because {良い影響}
* Bad, because {悪い影響・トレードオフ}

### Confirmation

{この決定どおりに実装されたことをどう確かめるか。例:コードレビュー、アーキテクチャテスト}

## Pros and Cons of the Options

### {選択肢1}

* Good, because {良い点}
* Neutral, because {どちらとも言えない点}
* Bad, because {悪い点}

### {選択肢2}

* Good, because {良い点}
* Bad, because {悪い点}

## More Information

{補足。関連する ADR、再検討する条件やタイミング、参考資料}

公式テンプレートでは、Decision Drivers、Consequences、Confirmation、Pros and Cons of the Options、More Information は省略可能な項目です。MADR には全項目版のほか、最小版(adr-template-minimal.md)と、説明文を除いた bare 版も用意されています。

テンプレート3:簡易版(日本語)

日本語の見出しで、Nygard 形式に「検討した選択肢」を足した形です。後述のスクリプトもこの形で ADR を作ります。

# 0000. タイトル(決めたことを短く)

- ステータス: 提案
- 日付: 2026-10-01
- 決定者: (名前またはチーム)

## 背景と課題

(なぜこの決定が必要になったのか。制約や数値があれば書く)

## 検討した選択肢

- 選択肢A
- 選択肢B

## 決定

「選択肢A」を採用する。理由は(記入)。

## 結果

- 良い点:
- 注意が必要な点:

記入例5選(完成形)

以下は、架空のタスク管理サービス「TaskBoard」(Web 画面と REST API、社内外のチームが利用)を題材にした記入例です。形式を変えて書いているので、テンプレートを選ぶ参考にもしてください。 製品名は例として挙げており、どれが優れているかを示すものではありません。

記入例1:DB 選定(MADR 形式)

---
status: accepted
date: 2026-04-10
decision-makers: バックエンドチーム(3名)
consulted: インフラ担当
informed: フロントエンドチーム
---

# タスク管理 API のメイン DB に PostgreSQL を使う

## Context and Problem Statement

TaskBoard では、タスク・ボード・コメント・担当者の関係を扱う。
タスクには利用者が自由に項目を足せる「カスタム項目」があり、構造が利用者ごとに異なる。
リレーションを中心にしつつ、自由な項目も検索できる DB はどれか。

## Decision Drivers

* タスクとボード・担当者の関係を外部キーで守りたい
* カスタム項目(利用者ごとに形が違う)を検索条件に使いたい
* チームの全員が SQL を読み書きできる
* マネージドサービスを選べる

## Considered Options

* PostgreSQL 16
* MySQL 8.4
* MongoDB 8.0

## Decision Outcome

Chosen option: "PostgreSQL 16", because リレーションを外部キーで守りつつ、カスタム項目を jsonb 列に入れて GIN インデックスで検索でき、すべての判断基準を満たすため。

### Consequences

* Good, because タスクと関連データの整合性を DB の制約で保てる
* Good, because カスタム項目の追加でテーブル定義を変えずに済む
* Bad, because jsonb の中身は型が保証されないため、アプリ側で JSON Schema による検証が必要になる

### Confirmation

マイグレーションのレビューで、外部キーと jsonb 列への GIN インデックスがあることを確認する。

## Pros and Cons of the Options

### PostgreSQL 16

* Good, because 外部キーと jsonb の両方が使える
* Good, because チーム全員が利用経験を持つ
* Neutral, because 運用時は VACUUM の監視が必要

### MySQL 8.4

* Good, because チームの利用経験がある
* Neutral, because JSON 型はあるが、JSON の中身の検索には生成列とインデックスの設計が必要

### MongoDB 8.0

* Good, because 利用者ごとに形が違うデータを自然に扱える
* Bad, because ボードと担当者の関係をアプリ側で守る必要がある

## More Information

カスタム項目の件数が1テナントあたり 100 を超えたら、検索性能を測り直して再検討する。

記入例2:認証方式(Nygard 形式)

# 3. Web 画面の認証にサーバー側セッションと Cookie を使う

## Status

Accepted(2026-04-24)

## Context

TaskBoard の Web 画面は同じドメインの API だけを呼ぶ。
スマートフォンアプリや外部からの API 利用は、当面の予定にない。
ログアウトや権限変更を、すぐに全端末へ反映したい。
JWT をブラウザに保存する方式も検討したが、有効期限内のトークンを即時に無効化するには失効リストの仕組みが別に必要になる。

## Decision

私たちは、ログイン後にサーバー側でセッションを作り、セッション ID を Cookie(HttpOnly、Secure、SameSite=Lax)で渡す方式を採用する。
セッションは Redis に保存し、有効期限は最終操作から 8 時間とする。

## Consequences

- ログアウトや権限変更はセッション削除で即時に反映できる。
- JavaScript から Cookie を読めないため、トークンの持ち出しを防ぎやすい。
- Cookie を使うため、状態を変える API には CSRF 対策(SameSite に加えて CSRF トークン)が必要になる。
- Redis が新たな依存になり、監視対象に加える。
- 外部からの API 利用が必要になったら、この ADR を見直す(API トークン方式を別の ADR で検討する)。

記入例3:ログ基盤(MADR 形式・短め)

---
status: accepted
date: 2026-05-15
decision-makers: インフラ担当、バックエンドチーム
---

# アプリケーションログの保存と検索に Grafana Loki を使う

## Context and Problem Statement

API サーバーは 6 台で、ログは各サーバーのファイルに出ている。
障害調査のたびに各サーバーへログインして grep しており、調査に時間がかかる。
ログを1か所に集め、リクエスト ID で横断検索できる仕組みが必要。

## Decision Drivers

* リクエスト ID・ステータスコードで絞り込めること
* 1日あたり約 20 GB のログを 30 日保持できる費用であること
* 既存の Grafana(メトリクス表示に利用中)から見られること

## Considered Options

* Grafana Loki
* OpenSearch
* クラウド事業者のログサービス

## Decision Outcome

Chosen option: "Grafana Loki", because ラベルだけをインデックスする方式のため保存費用を抑えられ、既存の Grafana から同じ画面で検索できるため。

### Consequences

* Good, because 障害調査をブラウザ1画面で行える
* Good, because 本文を全文インデックスしないため、保存容量あたりの費用が小さい
* Bad, because 本文の自由な全文検索は OpenSearch より遅い。調査はラベル(service、level、status)で絞ってから行う運用にする
* Bad, because ログは JSON 形式に統一する必要がある(アプリ側の対応が必要)

### Confirmation

ログ出力のレビューで、全サービスが request_id を含む JSON を出していることを確認する。

## More Information

全文検索の要望が増えた場合は、OpenSearch の併用を新しい ADR で検討する。

記入例4:フロントエンドの状態管理(簡易版)

# 0007. サーバーのデータは TanStack Query、画面の状態は Zustand で管理する

- ステータス: 承認
- 日付: 2026-06-03
- 決定者: フロントエンドチーム

## 背景と課題

TaskBoard の画面(React 19)では、API から取得したタスク一覧と、
フィルタの選択やモーダルの開閉といった画面だけの状態を、同じ Redux ストアで管理している。
API の再取得・キャッシュ・読み込み中の表示を毎回手で書いており、
1画面あたりの状態管理のコードが多く、レビューにも時間がかかっている。

## 検討した選択肢

- Redux Toolkit(RTK Query を含む)に統一する
- サーバーのデータを TanStack Query、画面の状態を Zustand に分ける
- React の Context と useReducer だけで管理する

## 決定

「サーバーのデータを TanStack Query、画面の状態を Zustand に分ける」を採用する。
理由は、サーバーのデータ(キャッシュ・再取得・楽観的更新)と画面の状態は性質が違い、
分けることでそれぞれの記述が短くなるため。
RTK Query も同等の機能を持つが、チームが TanStack Query の経験を持つことを重視した。

## 結果

- 良い点: 再取得やキャッシュの処理を自前で書かずに済む。画面の状態のストアが小さくなる
- 注意が必要な点: ライブラリが2つになるため、「どちらに置くか」の基準を README に書く
  (API から来るものは TanStack Query、それ以外は Zustand)
- 注意が必要な点: 既存の Redux ストアは画面ごとに段階的に移行し、完了まで併存させる

記入例5:API のバージョニング(MADR 形式)

---
status: accepted
date: 2026-07-22
decision-makers: API チーム
consulted: 外部連携の利用チーム(2チーム)
informed: サポート担当
---

# 公開 API のバージョンは URL パス(/v1/)で表す

## Context and Problem Statement

TaskBoard の REST API を外部チームにも公開する。
今後、互換性のない変更(フィールド名の変更など)が必要になったとき、
既存の利用者を壊さずに新しい形を出す方法を決めたい。

## Decision Drivers

* 利用者がブラウザや curl で簡単に試せること
* API ゲートウェイでバージョンごとにルーティングできること
* ドキュメント(OpenAPI)をバージョンごとに分けやすいこと

## Considered Options

* URL パス(/v1/tasks)
* リクエストヘッダー(Accept: application/vnd.taskboard.v1+json)
* クエリパラメータ(/tasks?version=1)

## Decision Outcome

Chosen option: "URL パス(/v1/tasks)", because URL を見るだけでバージョンがわかり、ゲートウェイのルーティングと OpenAPI の分割が最も簡単なため。

### Consequences

* Good, because 利用者がバージョンを取り違えにくい
* Good, because v1 と v2 を並行して動かせる
* Bad, because 同じリソースに複数の URL ができる。v2 公開後の v1 の提供期間を決める必要がある
* Bad, because 互換性のある変更(フィールド追加)ではバージョンを上げない、というルールを周知する必要がある

### Confirmation

OpenAPI の Lint で、すべてのパスが /v{数字}/ で始まることを CI で確認する。

## Pros and Cons of the Options

### リクエストヘッダー

* Good, because URL が変わらない
* Bad, because ブラウザや curl で試すときにヘッダー指定が必要

### クエリパラメータ

* Good, because 指定が簡単
* Bad, because 省略時の既定バージョンの扱いが曖昧になりやすい

## More Information

v2 を公開したら、v1 は 12 か月提供し、レスポンスヘッダーで終了予定日を知らせる。

記入例を書くときのポイント

項目 書き方のコツ 記入例での箇所
背景 数値や制約を入れる(「6 台」「1日約 20 GB」) 例3
選択肢 採用しなかった案も、良い点を含めて公平に書く 例1・例5
決定 「〜を採用する」と言い切り、理由を1文で添える 全例
結果 悪い点・新たに必要な作業も書く 例2(CSRF 対策、Redis 監視)
再検討の条件 「〜になったら見直す」を書いておく 例1・例2・例3

とくに「結果」に悪い点を書くことが大切です。 トレードオフが書いてあると、後から読む人が「わかったうえで選んだ」と理解でき、同じ議論を繰り返さずに済みます。

ステータスの運用

ADR は承認後に本文を書き換えず、ステータスだけを変えるのが基本です。

stateDiagram-v2
    state "提案" as Proposed
    state "承認" as Accepted
    state "却下" as Rejected
    state "置き換え済み" as Superseded
    state "廃止" as Deprecated
    [*] --> Proposed: プルリクエストを作成
    Proposed --> Accepted: レビューで合意しマージ
    Proposed --> Rejected: 採用しないと合意
    Accepted --> Superseded: 新しい ADR が承認された
    Accepted --> Deprecated: 対象の機能をなくした
ステータス 英語表記 意味 すること
提案 Proposed レビュー中 プルリクエストで議論する
承認 Accepted 決定した マージする。本文は以後書き換えない
却下 Rejected 採用しなかった ファイルは消さずに残す(なぜ採用しなかったかが資産になる)
置き換え済み Superseded by ADR-XXXX 新しい判断に替わった 新しい ADR へのリンクを書く
廃止 Deprecated 対象がなくなった 理由を1行添える

却下した ADR も残しておくと、「その案は以前に検討した」と経緯を示せます。誤字の修正やリンクの追加など、判断の内容を変えない修正は承認後でも行ってかまいません。

docs/adr/ に連番の ADR を作るスクリプト

番号の採番と置き換え時のステータス更新は、スクリプトに任せると手間とミスが減ります。 次の Python スクリプトは標準ライブラリだけで動きます(Python 3.11.15 で実行)。

#!/usr/bin/env python3
"""docs/adr/ に連番つきの ADR を作る小さなスクリプト(Python 3.9 以上、標準ライブラリのみ)

使い方:
  python3 adr.py new "ログ基盤に OpenSearch を採用する" --slug log-platform
  python3 adr.py new "認証を OIDC に変更する" --slug auth-oidc --supersedes 3
  python3 adr.py index
"""
import argparse
import datetime
import re
import sys
from pathlib import Path

ADR_DIR = Path("docs/adr")
FILE_RE = re.compile(r"^(\d{4})-(.+)\.md$")

TEMPLATE = """# {num:04d}. {title}

- ステータス: 提案
- 日付: {date}
- 決定者: (記入)
{supersedes_line}
## 背景と課題

(なぜこの決定が必要になったのかを書く)

## 検討した選択肢

- 選択肢A
- 選択肢B

## 決定

「選択肢A」を採用する。理由は(記入)。

## 結果

- 良い点:
- 注意が必要な点:
"""


def existing():
    ADR_DIR.mkdir(parents=True, exist_ok=True)
    items = []
    for p in sorted(ADR_DIR.iterdir()):
        m = FILE_RE.match(p.name)
        if m:
            items.append((int(m.group(1)), p))
    return items


def slugify(text):
    s = re.sub(r"[^a-z0-9]+", "-", text.lower()).strip("-")
    return s or "decision"


def find(num):
    for n, p in existing():
        if n == num:
            return p
    sys.exit(f"error: ADR {num:04d} が見つかりません")


def cmd_new(args):
    items = existing()
    num = (items[-1][0] + 1) if items else 1
    slug = args.slug or slugify(args.title)
    path = ADR_DIR / f"{num:04d}-{slug}.md"
    supersedes_line = ""
    if args.supersedes:
        old = find(args.supersedes)
        supersedes_line = f"- 置き換え対象: [{old.stem}]({old.name})\n"
        text = old.read_text(encoding="utf-8")
        text, n = re.subn(r"^- ステータス: .*$",
                          f"- ステータス: 置き換え済み([{path.stem}]({path.name}))",
                          text, count=1, flags=re.M)
        if n == 0:
            sys.exit(f"error: {old.name} にステータス行がありません")
        old.write_text(text, encoding="utf-8")
        print(f"updated: {old}")
    path.write_text(TEMPLATE.format(
        num=num, title=args.title, date=datetime.date.today().isoformat(),
        supersedes_line=supersedes_line), encoding="utf-8")
    print(f"created: {path}")


def cmd_index(args):
    rows = ["# ADR 一覧", "", "| 番号 | タイトル | ステータス |", "| --- | --- | --- |"]
    for num, p in existing():
        if p.name == "README.md":
            continue
        text = p.read_text(encoding="utf-8")
        title = text.splitlines()[0].lstrip("# ").split(". ", 1)[-1]
        m = re.search(r"^- ステータス: (.*)$", text, flags=re.M)
        status = m.group(1) if m else "不明"
        rows.append(f"| [{num:04d}]({p.name}) | {title} | {status} |")
    (ADR_DIR / "README.md").write_text("\n".join(rows) + "\n", encoding="utf-8")
    print("\n".join(rows))


def main():
    ap = argparse.ArgumentParser(description="ADR を連番で作成する")
    sub = ap.add_subparsers(dest="cmd", required=True)
    n = sub.add_parser("new", help="新しい ADR を作る")
    n.add_argument("title")
    n.add_argument("--slug", help="ファイル名に使う英小文字とハイフン")
    n.add_argument("--supersedes", type=int, help="置き換える ADR の番号")
    n.set_defaults(func=cmd_new)
    i = sub.add_parser("index", help="docs/adr/README.md に一覧を書き出す")
    i.set_defaults(func=cmd_index)
    args = ap.parse_args()
    args.func(args)


if __name__ == "__main__":
    main()

実行結果

空のリポジトリで ADR を4件作り、4件目で3件目を置き換えました。

$ python3 adr.py new "ADR を使って設計判断を記録する" --slug record-architecture-decisions
created: docs/adr/0001-record-architecture-decisions.md

$ python3 adr.py new "タスク管理APIのDBに PostgreSQL を採用する" --slug database
created: docs/adr/0002-database.md

$ python3 adr.py new "認証にセッションCookie方式を採用する" --slug auth-session
created: docs/adr/0003-auth-session.md

$ python3 adr.py new "認証を OIDC(外部IdP)に移行する" --slug auth-oidc --supersedes 3
updated: docs/adr/0003-auth-session.md
created: docs/adr/0004-auth-oidc.md

$ ls docs/adr
0001-record-architecture-decisions.md
0002-database.md
0003-auth-session.md
0004-auth-oidc.md

置き換えられた 0003 と、新しい 0004 の先頭部分です。

$ cat docs/adr/0003-auth-session.md
# 0003. 認証にセッションCookie方式を採用する

- ステータス: 置き換え済み([0004-auth-oidc](0004-auth-oidc.md))
- 日付: 2026-10-01
- 決定者: (記入)

$ cat docs/adr/0004-auth-oidc.md
# 0004. 認証を OIDC(外部IdP)に移行する

- ステータス: 提案
- 日付: 2026-10-01
- 決定者: (記入)
- 置き換え対象: [0003-auth-session](0003-auth-session.md)

--slug を省略すると、英数字のタイトルからファイル名を作ります。 日本語だけのタイトルでは英数字が残らないため decision になるので、日本語タイトルのときは --slug を付けます。存在しない番号を指定したときはエラーで止まります。

$ python3 adr.py new "Log Platform Selection"
created: docs/adr/0005-log-platform-selection.md

$ python3 adr.py new "x" --supersedes 9
error: ADR 0009 が見つかりません

index で一覧を docs/adr/README.md に書き出せます。GitHub で docs/adr/ を開くと、この一覧が表示されます。

$ python3 adr.py index
# ADR 一覧

| 番号 | タイトル | ステータス |
| --- | --- | --- |
| [0001](0001-record-architecture-decisions.md) | ADR を使って設計判断を記録する | 提案 |
| [0002](0002-database.md) | タスク管理APIのDBに PostgreSQL を採用する | 提案 |
| [0003](0003-auth-session.md) | 認証にセッションCookie方式を採用する | 置き換え済み([0004-auth-oidc](0004-auth-oidc.md)) |
| [0004](0004-auth-oidc.md) | 認証を OIDC(外部IdP)に移行する | 提案 |
| [0005](0005-log-platform-selection.md) | Log Platform Selection | 提案 |

番号の重複を CI で見つける

2人が同時に ADR を作ると、同じ番号が2つできることがあります。 次のシェルスクリプトを CI で実行すると、マージ前に気づけます(bash 5.2 で実行)。

#!/usr/bin/env bash
# docs/adr の番号の重複とステータス行の有無を確認する
set -euo pipefail
cd docs/adr
dup=$(ls [0-9][0-9][0-9][0-9]-*.md | cut -c1-4 | sort | uniq -d)
if [ -n "$dup" ]; then
  echo "番号が重複しています: $dup"; ls ${dup}-*.md; exit 1
fi
for f in [0-9][0-9][0-9][0-9]-*.md; do
  grep -q '^- ステータス: ' "$f" || { echo "ステータス行がありません: $f"; exit 1; }
done
echo "OK: $(ls [0-9][0-9][0-9][0-9]-*.md | wc -l) 件"

0005 が2つある状態と、片方を 0006 に直した後の結果です。

$ ./check_adr.sh; echo "exit=$?"
番号が重複しています: 0005
0005-frontend-state.md
0005-log-platform-selection.md
exit=1

$ ./check_adr.sh; echo "exit=$?"
OK: 6 件
exit=0

Node.js 環境なら、npm の adr-log や、Homebrew などで入る adr-tools(シェル製)といった既存ツールを使う方法もあります。上のスクリプトは依存がなく、日本語の簡易版テンプレートに合わせて自由に変えられるのが利点です。

プルリクエストで ADR を回す流れ

ADR はコードと同じリポジトリに置き、プルリクエストでレビューすると、議論と決定が1か所に残ります。

手順 すること ポイント
1. 起票 python3 adr.py new "..." --slug ... でファイルを作り、ステータス「提案」でプルリクエストを出す 実装より先に出すと、手戻りが少ない
2. レビュー 関係者をレビュアーに指定し、コメントで議論する 反対意見や別案は「検討した選択肢」に追記する
3. 決定 合意できたらステータスを「承認」(却下なら「却下」)にしてマージ 決定日を「日付」に書く
4. 実装 実装のプルリクエストから ADR へリンクする 「ADR-0007 に基づく」と書くとレビューが速い
5. 置き換え 判断を変えるときは新しい ADR を起票し、承認時に --supersedes で古い方を更新 古い ADR の本文は書き換えない

プルリクエストのテンプレート(.github/pull_request_template.md)に次の欄を入れておくと、ADR を書くきっかけを逃しにくくなります。

## 設計判断

- [ ] この変更に、ADR に残すべき判断は含まれていない
- [ ] ADR を追加・更新した(docs/adr/XXXX-....md)

レビューの観点は「選択肢と理由が書かれているか」「結果に悪い点も書かれているか」の2点に絞ると、短時間でも質を保てます。

よくあるつまずきと対策

よくある状態 起きること 対策
実装が終わってから書く 検討した選択肢や理由を思い出せず、「決定」だけの記録になる 判断の前に「提案」で起票し、議論しながら埋める
理由が書かれていない 「なぜ」がわからず、同じ議論が繰り返される 「〜を採用する。理由は〜」の形をテンプレートで固定し、レビュー観点にする
選択肢が1つしかない 比較した形跡がなく、決定の説得力が弱くなる 「何もしない」「現状維持」も選択肢として書く
悪い点が書かれていない 後から問題が起きたとき、想定内か判断できない 「結果」に悪い点を最低1つ書くルールにする
状況が変わっても更新されない 承認済みの ADR と実際の構成が食い違う 置き換えの ADR を作る。再検討の条件を「More Information」に書いておく
承認済みの本文を書き換える 当時の判断の経緯が失われる 本文は残し、新しい ADR で置き換える
1つの ADR に複数の判断を入れる 一部だけ変えたいときに置き換えにくい 判断1つにつき1ファイルにする
長くなりすぎる 読まれなくなる 設計の詳細は Design Doc や設計書に分け、ADR からリンクする
どこにあるかわからない 書いても参照されない docs/adr/README.md に一覧を置き、ルートの README からリンクする

最初の1本として「ADR を使って設計判断を記録する」という ADR を書くのもおすすめです。ADR を導入する理由、置き場所、テンプレート、ステータスの運用をその中で決めておくと、ルールそのものが ADR として残ります。

よくある質問

Q. ADR は日本語で書いてもよいですか? A. 問題ありません。読む人が最も理解しやすい言語で書くのが大切です。MADR の見出しを英語のまま使い、本文を日本語で書く方法もあります。チーム内でどちらかに統一すると一覧が読みやすくなります。

Q. ADR はどこに置くのがよいですか? A. コードと同じリポジトリの docs/adr/(MADR では docs/decisions/ が例示されています)に置くのが一般的です。コードと一緒にレビューでき、ブランチやタグで当時の ADR も辿れます。複数のリポジトリにまたがる判断は、共通のドキュメント用リポジトリに置き、各リポジトリからリンクします。

Q. 既存のシステムで、過去の判断もさかのぼって ADR にすべきですか? A. すべてを書く必要はありません。よく質問される判断や、近いうちに見直しそうな判断から、わかる範囲で書きます。その場合は「日付」に記録した日を書き、背景に「当時の資料から再構成した」と添えておくと誤解がありません。

まとめ

  • ADR は「なぜそう決めたか」を1判断1ファイルで残す短い文書。設計書は「今どうなっているか」を担う
  • 後から変えにくく、複数の選択肢を比べた判断は ADR に書く。迷ったら簡易版で書く
  • テンプレートは Nygard 形式・MADR 4.0・簡易版の3種類から、チームで1つに統一する
  • 「結果」には悪い点や新たに必要な作業も書くと、後から読む人が経緯を理解できる
  • 承認後は本文を書き換えず、新しい ADR で置き換える。却下した ADR も残す
  • 採番と置き換えはスクリプト、番号の重複は CI に任せると運用が続けやすい
  • プルリクエストで提案し、マージで承認すると、議論と決定が1か所に残る

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「ADRの書き方|テンプレートと記入例5選・書くべき判断基準」

株式会社RJCでは、設計判断を言葉にして残す方法を仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?