ADR の運用 — Git での ADR 管理
3ヶ月後の悩み
ADRを始めて3ヶ月。チームには30本近くのADRが溜まってきた。
コウスケがSlackに書いた。
「Redisのキャッシュ設定を変えようとしてるんだけど、過去のADRにキャッシュ関係のが何本かある。どれが最新か分からなくなってきた……」
シンジはADRのディレクトリを見た。確かに混乱している。
docs/adr/
├── 002-redis-cache.md
├── 008-redis-cluster.md
├── 017-cache-invalidation-strategy.md
├── 024-redis-upgrade.md
├── 029-cache-ttl-policy.md
「Redisだけで5本ある。それぞれの関係性が分からない。ADR-002とADR-024はどっちが最新?」
「ADR-024はADR-002のクラスター化で、ADR-008はADR-002のアーキテクチャの変更で……」とリョウが説明しようとして止まった。「確かにパッと見て分からない」
「インデックスと関連付けの仕組みが必要だ。ADRが溜まってきたら、管理の自動化も考えないといけない」
adr-toolsの導入
シンジはまず、ADRの管理を自動化するCLIツール「adr-tools」を導入した。
# macOSでのインストール
brew install adr-tools
# Linuxの場合(GitHub Releasesから)
curl -Lo adr-tools https://github.com/npryce/adr-tools/releases/latest/download/adr-tools
chmod +x adr-tools
sudo mv adr-tools /usr/local/bin/
# バージョン確認
adr --version
# プロジェクトの初期化
cd my-rails-app
adr init docs/adr
# → docs/adr/ ディレクトリが作成され、0001-record-architecture-decisions.md が生成される初期化で生成されるADR-0001(adr-toolsの規約):
# 1. Record architecture decisions
Date: 2024-01-15
## 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.新しいADRを作成:
# 新しいADRを作成(連番は自動)
adr new "PostgreSQLのconnection poolingにPgBouncerを採用する"
# → docs/adr/031-postgresqlのconnection-poolingにpgbouncerを採用する.md が作成される
# 既存ADRをsupersede(上書き)する新しいADRを作成
# -s オプションで上書くADRの番号を指定
adr new -s 2 "Redisクラスター構成への移行とコネクション設定更新"
# → ADR-002のStatusが自動的に "Superseded by ADR-031" に変更される
# → 新しいADR-031が作成される
# リンクを追加(相互参照)
adr link 031 "Requires" 008 "Is required by"
# → ADR-031とADR-008に相互参照が追加されるadr-toolsが生成するテンプレート:
# 31. PostgreSQLのconnection poolingにPgBouncerを採用する
Date: 2024-07-15
## Status
Accepted
## Context
[コンテキストを書く]
## Decision
[決定を書く]
## Consequences
[結果を書く]INFO
adr-toolsはADRの連番管理、Supersede(上書き)の自動処理、インデックスファイルの生成を自動化してくれる。手動管理ではミスが起きやすい「どのADRが有効か」を確実に追跡できる。
インデックスファイルの整備
30本近くになったADRを整理するために、インデックスファイルを作った。
# adr-toolsでインデックス(目次)を自動生成
adr generate toc > docs/adr/README.md生成されたインデックス(自動):
# Architecture Decision Records
* [1. Record architecture decisions](0001-record-architecture-decisions.md)
* [2. RedisをキャッシュレイヤーとしてElastiCacheで運用する](002-redis-cache.md)
* Superseded by [24. Redisクラスターへのアップグレード](024-redis-upgrade.md)
* [8. データベースをMySQL 8.0からPostgreSQL 14に移行する](008-postgresql-migration.md)
* [12. 認証方式をJWT + Refresh Tokenに移行する](012-jwt-refresh-token-auth.md)
...さらに、シンジはカテゴリ別の整理も追加した(手動で編集):
# ADR Index
## はじめに
このドキュメントはADR(アーキテクチャ意思決定記録)の索引です。
新しいADRを追加した後は `adr generate toc > docs/adr/README.md` を実行してください。
## カテゴリ別索引
### インフラ・クラウド
| ADR | タイトル | Status |
|-----|---------|--------|
| [ADR-006](006-ecs-fargate.md) | ECS Fargateの採用 | Accepted |
| [ADR-015](015-ecs-vs-eks.md) | EKS移行の見送り | Accepted |
| [ADR-024](024-redis-cluster.md) | Redisクラスターへのアップグレード | Accepted |
### データベース
| ADR | タイトル | Status |
|-----|---------|--------|
| [ADR-002](002-redis-cache.md) | RedisキャッシュにElastiCacheを採用 | Superseded by 024 |
| [ADR-008](008-postgresql-migration.md) | MySQLからPostgreSQLへの移行 | Accepted |
| [ADR-011](011-rds-read-replica.md) | RDSリードレプリカの追加 | Accepted |
### 認証・セキュリティ
| ADR | タイトル | Status |
|-----|---------|--------|
| [ADR-004](004-devise-authentication.md) | Deviseによる認証 | Superseded by 012 |
| [ADR-012](012-jwt-auth.md) | JWT + Refresh Token認証方式 | Accepted |
### アプリケーション・フレームワーク
| ADR | タイトル | Status |
|-----|---------|--------|
| [ADR-001](001-rails-framework.md) | Ruby on Railsの採用 | Accepted |
| [ADR-003](003-goodjob.md) | GoodJobによる非同期処理 | Superseded by 031 |
| [ADR-021](021-kaminari.md) | Kaminariのページネーション | Accepted |
| [ADR-031](031-sidekiq-migration.md) | GoodJobからSidekiqへの移行 | Accepted |
## 現在有効なADR一覧(Acceptedのみ)
最後に更新: 2024年7月「これで全体像が掴める」とコウスケが言った。「カテゴリ別に見れるのが特にいい」
PRとADRの連携
次の課題はADRとPRを紐づけることだった。
「新しい設計判断が生まれたとき、PRとADRがセットになるようにしたい」
シンジはGitHubのPRテンプレートを修正した。
<!-- .github/pull_request_template.md -->
## 変更内容
<!-- 何を変えたか。1〜3文で簡潔に -->
## 変更の背景・理由
<!-- なぜこの変更が必要だったか -->
## 技術的な変更を伴う場合(アーキテクチャ・設計判断)
- [ ] このPRに関連するADRを追加した(新しい設計判断)
- ADR番号: `docs/adr/XXX-*.md`
- [ ] 既存のADRを更新した(設計変更)
- 更新したADR: `docs/adr/XXX-*.md`
- [ ] 既存のADRを参照するコードコメントを追加した
- [ ] ADRは不要と判断した(理由: )
## テスト
- [ ] ユニットテストを追加・更新した
- [ ] ステージング環境で動作確認した
- [ ] パフォーマンスへの影響を確認した(変更がある場合)
## レビューポイント
<!-- レビュアーに特に見てほしい点 -->実際のPRがこんな形になった:
# PR #342: PostgreSQLのコネクションプール設定をPgBouncerで最適化
## 変更内容
PgBouncerをEC2で運用し、コネクションプーリングを最適化する。
RDSへの最大コネクション数を現在の300から100に削減。
## 変更の背景・理由
RDSのコネクション数が上限(500)の60%を常時超えており、
スパイク時にコネクション待ちが発生していた(INC-043参照)。
## 技術的な変更を伴う場合
- [x] このPRに関連するADRを追加した
- ADR番号: `docs/adr/031-postgresql-connection-pooling.md`
- [x] 既存のADRを参照するコードコメントを追加した
## レビューポイント
- PgBouncerの`pool_mode = transaction`が適切か確認をお願いします
(`session`モードとのトレードオフはADR-031を参照)
- ADR-031のConsequencesを読んでからコードレビューしてくださいこれにより、コードの変更とその判断理由が常にセットで記録されるようになった。
GitHub Actionsでのバリデーション
シンジはさらに、ADRのバリデーションをCI/CDに組み込んだ。
# .github/workflows/adr-validation.yml
name: ADR Validation
on:
pull_request:
paths:
- 'docs/adr/**'
- 'app/**'
- 'config/**'
jobs:
validate-adr:
name: ADRフォーマット検証
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: ADRの必須セクションをチェック
run: |
ERRORS=0
for file in docs/adr/*.md; do
# インデックスファイルは除外
if [[ "$file" == *"README"* ]]; then
continue
fi
echo "チェック中: $file"
# 必須セクションの確認
if ! grep -q "^## Status" "$file"; then
echo "ERROR: $file - '## Status' セクションがありません"
ERRORS=$((ERRORS + 1))
fi
if ! grep -q "^## Context" "$file"; then
echo "ERROR: $file - '## Context' セクションがありません"
ERRORS=$((ERRORS + 1))
fi
if ! grep -q "^## Decision" "$file"; then
echo "ERROR: $file - '## Decision' セクションがありません"
ERRORS=$((ERRORS + 1))
fi
if ! grep -q "^## Consequences" "$file"; then
echo "ERROR: $file - '## Consequences' セクションがありません"
ERRORS=$((ERRORS + 1))
fi
# Statusの値が有効かチェック
STATUS=$(grep "^## Status" -A1 "$file" | tail -1)
if [[ "$STATUS" != "Proposed" && \
"$STATUS" != "Accepted" && \
"$STATUS" != "Deprecated" && \
! "$STATUS" =~ ^Superseded ]]; then
echo "WARN: $file - Statusの値 '$STATUS' が標準外です"
fi
done
if [ $ERRORS -gt 0 ]; then
echo "ADRバリデーション失敗: ${ERRORS}件のエラー"
exit 1
fi
echo "ADRバリデーション成功"
- name: ADRインデックスが最新かチェック
run: |
# adr-toolsをインストール
npm install -g adr-log
# 現在のインデックスを生成して比較
adr-log -d docs/adr > /tmp/expected_toc.md
if ! diff docs/adr/README.md /tmp/expected_toc.md > /dev/null 2>&1; then
echo "ERROR: docs/adr/README.mdが最新ではありません"
echo "以下を実行してください: adr generate toc > docs/adr/README.md"
diff docs/adr/README.md /tmp/expected_toc.md
exit 1
fi
echo "ADRインデックス確認OK"
check-adr-in-pr:
name: PR内のADR参照チェック
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: 設計判断を含むPRにADRが含まれているかチェック
run: |
# 変更されたファイルを取得
CHANGED_FILES=$(git diff --name-only origin/${{ github.base_ref }}...HEAD)
# アーキテクチャに影響するファイルが変更されているか確認
ARCH_CHANGES=$(echo "$CHANGED_FILES" | grep -E \
"config/database.yml|config/environments|Gemfile|Dockerfile|terraform/" || true)
if [ -n "$ARCH_CHANGES" ]; then
echo "アーキテクチャ関連ファイルの変更を検知:"
echo "$ARCH_CHANGES"
# ADRファイルが含まれているか確認
ADR_CHANGES=$(echo "$CHANGED_FILES" | grep "docs/adr/" || true)
if [ -z "$ADR_CHANGES" ]; then
echo "WARNING: アーキテクチャ関連ファイルが変更されていますが"
echo "ADRが含まれていません。ADRが必要か確認してください。"
echo ""
echo "ADRが不要な場合はPRの説明に理由を記載してください。"
# エラーにはしない(警告のみ)
fi
fi「ADRのフォーマットが正しくないとCIが通らない」とシンジが言った。「書く習慣と、書いたものが正しいことの担保を自動化できる」
WARNING
CIでの強制は「セーフティネット」だ。文化が根付く前の初期段階では有効だが、チームが習慣化したらルールを緩めても良い。最初から厳しくしすぎると「ADRは面倒くさい」という印象を与えてしまう。
ADRの検索性を高める
30本を超えてくると、検索が重要になる。
コマンドラインでの検索
# "redis"に関するADRをファイル名で検索
ls docs/adr/ | grep -i redis
# 全ADRのステータスを一覧確認
grep -r "^Accepted\|^Deprecated\|^Superseded" docs/adr/*.md \
| sed 's|docs/adr/||' | sed 's|.md:| → |'
# "Accepted"なADRだけリスト
grep -l "^Accepted$" docs/adr/*.md | grep -v README | sort
# Supersededになっているものを見つける
grep -rl "^Superseded" docs/adr/ | grep -v README内容での検索(ripgrepを使用)
# "ElastiCache"に言及しているADRを検索(ファイル名とコンテキスト付き)
rg "ElastiCache" docs/adr/ --type md
# "Sidekiq"について書かれたADRのタイトルを表示
rg "^# \d+\." docs/adr/ --type md | grep -i sidekiq
# "再評価のトリガー"セクションを持つADRを検索
rg "再評価のトリガー" docs/adr/ -l
# 特定日付以降のADRを検索
rg "Date: 2024-0[7-9]" docs/adr/ --type md
# 特定の技術(PostgreSQL)が意思決定に含まれるADRを全て表示
rg "PostgreSQL" docs/adr/ -l | xargs -I{} sh -c 'echo "=== {} ===" && head -3 {}'GitHubのUIでの検索
# リポジトリ内での高度な検索
# GitHubのコード検索(リポジトリ内)
# 例: docs/adr/ ディレクトリでredisを含むファイルを検索
# → path:docs/adr redis
# 特定ステータスのADRを検索
# → path:docs/adr "## Status" "Accepted"
# 特定月のADRを検索
# → path:docs/adr "Date: 2024-07"ADRのライフサイクル管理
ADRは「一度書いたら終わり」ではない。技術の変化に合わせてメンテナンスが必要だ。
定期的なADRレビュー(四半期)
シンジはチームに「四半期ADRレビュー」を設けた。所要時間: 45分〜1時間。
# 四半期ADRレビューのアジェンダ(テンプレート)
**実施日**: XXXX年XX月XX日
**参加者**: チーム全員
**ファシリテーター**: (持ち回り)
## 目的
- 現状と乖離したADRを更新する
- Deprecated/Supersededにすべきものを処理する
- 新しいADRが必要な判断がないか確認する
- ADRの文化を振り返る(何が上手くいっているか、改善点は何か)
## レビュー項目(事前準備)
- [ ] 前回レビュー以降に追加されたADRを全員が読む
- [ ] 現在"Accepted"のADRが実際に運用中か各担当者が確認
- [ ] 使われなくなった技術・方式がないか確認
## 議論項目
1. 各ADRのステータス確認(10分)
- Superseded/Deprecatedにすべきものはないか?
- 「再評価のトリガー」に書いた条件が発動していないか?
2. 新規ADRの確認(20分)
- 前回以降の意思決定でADRになっていないものはないか?
- 過去に「ADR不要」とした判断を見直すものはないか?
3. ADR文化の振り返り(10分)
- 「ADRがあってよかった」瞬間はあったか?
- 「ADRがなくて困った」瞬間はあったか?
- ADRの書き方に改善点はあるか?
## アクションアイテム
(レビュー後に記録)実際の四半期レビューで出たアクションアイテム:
## 2024年Q3 レビュー結果
### ステータス変更
- ADR-003 (GoodJob): Superseded by ADR-031 ✅
- ADR-004 (Devise): Superseded by ADR-012 ✅
- ADR-002 (Redis基本設定): Superseded by ADR-024 ✅
### 新規作成が必要なADR
- GoodJobからSidekiqへの移行(担当: リョウ)→ ADR-031
- RDSリードレプリカ追加(担当: コウスケ)→ ADR-032
### ADRなし判断の確認
- Datadogの導入: ADR不要と判断(モニタリングツールは変更頻度低い)
→ 今後Datadogに重大な問題が出た場合は改めてADRを書く
### 文化の振り返り
- 良かった点: ナオ(新入り)がADRを読んでオンボーディングが早かった
- 改善点: ADRのレビューがPRで後回しになりがち。ADRを先に書く文化にしたいSuperseded の適切な処理
# GoodJob(ADR-003)をSidekiq(ADR-031)でSupersededにする
adr new -s 3 "GoodJobからSidekiqへの移行"adr-toolsはADR-003を自動的に更新する:
# ADR-003: GoodJobによる非同期処理(更新後)
## Status
Superseded by [ADR-031. GoodJobからSidekiqへの移行](031-goodjob-to-sidekiq-migration.md)
(以下は変更なし)
## Context
...WARNING
古いADRを削除してはいけない。SupersededやDeprecatedになっても残す。「なぜGoodJobをやめてSidekiqにしたか」は、GoodJobを選んだ歴史とセットで意味を持つ。削除されたADRは「なぜそうなっているか」を永遠に失わせる。
ADRのコミットメッセージルール
チームはコミットメッセージにもADR番号を含めるルールを設けた。
# 良いコミットメッセージの例
git commit -m "feat: PgBouncerでコネクションプールを最適化 (ADR-031)"
git commit -m "docs: ADR-031 PostgreSQL connection pooling方式を追加"
git commit -m "refactor: JWT有効期限を15分に変更 (ref: ADR-012)"
git commit -m "chore: Sidekiqへの移行完了。GoodJob関連コードを削除 (ADR-031)"
# ADR-031に関連するコミット一覧を確認
git log --oneline --grep="ADR-031"
# 出力例:
# a1b2c3d feat: PgBouncerでコネクションプールを最適化 (ADR-031)
# b2c3d4e docs: ADR-031 PostgreSQL connection pooling方式を追加これにより git log --grep="ADR-012" でADR関連のコミットが追える。
ADRをGitのタグで管理する
チームはADRの大きな節目をGitタグで管理することにした。
# ADRのマイルストーンをタグで記録
git tag -a "adr/v1.0" -m "ADR文化導入: 最初の10本のADRが揃った"
git tag -a "adr/v2.0" -m "ADR運用改善: インデックス・CIバリデーション追加"
git tag -a "adr/infra-2024" -m "2024年インフラADR群: ECS最適化・PgBouncer"Railsプロジェクトでの最終的なADRディレクトリ
3ヶ月の運用を経て、シンジのチームのADRディレクトリはこうなった。
docs/
└── adr/
├── README.md # インデックス(一部自動生成)
├── 0001-record-architecture-decisions.md # adr-tools標準
├── 001-rails-framework.md # Accepted
├── 002-redis-cache.md # Superseded by 024
├── 003-goodjob-background-jobs.md # Superseded by 031
├── 004-devise-authentication.md # Superseded by 012
├── 005-aws-s3-file-storage.md # Accepted
├── 006-ecs-fargate.md # Accepted
├── 007-stripe-payment.md # Accepted
├── 008-postgresql-migration.md # Accepted
├── 009-postgis-adoption.md # Accepted
├── 010-sidekiq-redis.md # Accepted
├── ...(中略)...
├── 012-jwt-refresh-token-auth.md # Accepted
├── 015-ecs-vs-eks.md # Accepted
├── 021-kaminari-pagination.md # Accepted
├── 024-redis-cluster-upgrade.md # Accepted
├── 029-cache-ttl-policy.md # Accepted(インシデント後更新)
├── 031-sidekiq-migration.md # Accepted
└── templates/
└── adr-template.md # チーム用テンプレート
チームのテンプレート:
# ADR-XXX: [タイトル]
Date: YYYY-MM-DD
Author: [著者名]
Status: Proposed
## Status
Proposed
## Context
### 問題の背景
[どんな問題が起きていたか。具体的な数字を含める]
### 技術的制約
- チームスキル:
- コスト制約:
- スケジュール制約:
### 検討した選択肢
1. [選択肢A]
2. [選択肢B]
3. [選択肢C]
## Decision
[決定内容を1文で]
### 選んだ理由
-
### 選ばなかった理由
**[選択肢B] を選ばなかった理由:**
**[選択肢C] を選ばなかった理由:**
## Consequences
### ポジティブな影響
-
### ネガティブな影響・リスク
-
### 実装での注意事項
-
## 再評価のトリガー
- [この判断を見直すべき条件]
## 関連ドキュメント
- [関連するADR番号・RFC・Design Doc]コウスケがSlackに書いた。「Redisのキャッシュ設定を変えようとしてたんだけど、ADR-002, 024, 029を読んで状況が完全に理解できた。コードを見るより早かった」
シンジはその投稿をSlackでピン留めした。「これがADRが機能している証拠だ」
次の章では、ADRをチームの文化として定着させる方法と、RFC・Design Docとの使い分け、意思決定プロセスの民主化と心理的安全性への影響を学ぶ。