ADRテンプレートと記入例:コピーしてすぐ使えるフォーマット
会議は2時間続いていました。新しいサービスにPostgreSQLとMongoDBのどちらを使うかで議論していたのです。リレーショナルな整合性、柔軟なスキーマ、チームの習熟度、運用の複雑さ。意見が飛び交っていました。
そのとき誰かが言いました。「去年、ユーザーサービスでもまったく同じ議論をしなかったっけ?」
沈黙。していたのです。1年前、私たちは同じ問いにまったく同じだけの時間をかけ、PostgreSQLを選んでいました。その理由を誰も覚えておらず、議論を主導したエンジニアはすでに退職していました。だから私たちは、同じ議論をゼロからやり直していたのです。
それをきっかけに、私たちはアーキテクチャ決定記録(ADR)を書き始めました。この記事はコピー&ペースト用のパートです。3つのテンプレート、それぞれを使う場面、そして私たち自身の3つの決定を記入した例を紹介します。
ADRとは?
アーキテクチャ決定記録は、1つのアーキテクチャ上の決定を記録する短いドキュメントです。システムのすべてを網羅する設計書ではなく、1つの決定だけを扱います。
- 何を決めたか
- なぜそう決めたか
- どんな代替案を検討したか
- どんな結果がもたらされるか
ADRは1〜2ページに収めるべきです。それより長くなるなら、おそらく複数の決定を記録しています。いつ書くべきか、ライフサイクル、チームが陥りがちな間違いについては完全ガイドで扱っています。この記事はフォーマットに絞ります。
最小限のテンプレート(Nygard)
今でもほとんどのチームが使っているフォーマットは、Michael Nygardが2011年の記事で提案したものです。構成は5つ。短い名詞句のタイトル、ステータス、コンテキスト(「作用している力」)、決定(「それらの力に対する私たちの応答」)、そして結果(「決定を適用した後に生じるコンテキスト」)です。彼が定めたステータスは、Proposed、Accepted、Deprecated、Supersededです。
# ADR-NNNN: 短い名詞句
## ステータス
Proposed | Accepted | Deprecated | Superseded by ADR-XXXX
## コンテキスト
どんな力が働いているか? 技術、組織、締め切り、予算。
具体的に:数値、制約、影響を受ける人。
## 決定
私たちは〜する。
## 結果
何が簡単になり、何が難しくなり、今後何に注意すべきか。
私たちはこれに1つのセクションを加えています。決定と結果のあいだに置く検討した代替案です。後になって最も時間を節約してくれるのがこの部分です。1年後に聞かれるのは「何を選んだのか?」ではなく、たいてい「なぜXを選ばなかったのか?」だからです。
## 検討した代替案
### 選択肢B
- 長所: ...
- 短所: ...
- 却下した理由: ...
MADR
MADR(Markdown Architectural Decision Records)は、adr.github.io/madrでメンテナンスされている、より構造化されたテンプレートです。現在のバージョンは2024年9月17日にリリースされた4.0.0です。メタデータをYAMLのフロントマターに置き、選択肢とそのトレードオフを文章ではなく明示的なセクションにしています。
---
status: proposed | rejected | accepted | deprecated | superseded by ADR-XXXX
date: YYYY-MM-DD
decision-makers: ...
consulted: ...
informed: ...
---
# 解決した問題と選んだ解決策を表す短いタイトル
## コンテキストと問題設定
2〜3文、または短いストーリー。できれば問いの形で書く。
## 決定要因
* 要因1
* 要因2
## 検討した選択肢
* 選択肢A
* 選択肢B
* 選択肢C
## 決定の結果
選んだ選択肢:「選択肢A」。理由は〜。
### 結果
* 良い点:〜だから
* 悪い点:〜だから
### 確認方法
決定が実装されていることをどう確認するか(レビュー、テスト、フィットネス関数)。
## 各選択肢の長所と短所
### 選択肢A
* 良い点:〜だから
* 悪い点:〜だから
## 補足情報
リンク、関連する決定、見直す時期。
MADRでは、検討した選択肢と決定の結果以降のセクションはすべて任意です。空の見出しを残すのではなく、使わないものは削除しましょう。
Y-statement
Y-statementは、1つの決定を1文に収めます。Uwe Zdun、Olaf Zimmermannらによるものです(「Sustainable Architectural Design Decisions」、IEEE Software、2013年)。
<ユースケースまたはコンポーネント>のコンテキストにおいて、
<関心事または要件>に直面し、
私たちは<選択肢>を選び
(<他の選択肢>を退け)、
<品質または便益>を実現するために、
<欠点>を受け入れる。
完全なコンテキストのセクションを載せるには短すぎますが、それこそが狙いです。1ページに値しない決定や、長いADRの冒頭に置く要約行、あるいは決定ログの記入形式として使いましょう。
どのフォーマットを使うか
| フォーマット | 使う場面 | 注意点 |
|---|---|---|
| Nygard(+代替案) | 始めたばかり、またはほとんどの決定に明らかな勝者と却下された選択肢がいくつかある場合 | コンテキストのセクションが論文のように膨らむこと |
| MADR | 決定に異論がある、複数人の承認が必要、あるいは選択肢と決定要因をADR間で比較可能にしたい場合 | 習慣で任意のセクションをすべて埋めてしまうこと |
| Y-statement | 小さな決定、決定ログ、または長いADRの冒頭に置く1行の要約 | 本格的なコンテキストのセクションが必要な決定に使ってしまうこと |
リポジトリごとに1つを選び、それを守りましょう。フォーマットが混在していると、どれか1つのフォーマットで書く手間よりも、読み流す手間のほうが大きくなります。
例1:ADR-0042、注文サービスにPostgreSQLを使う
冒頭のエピソードの決定を、Nygardのフォーマットに私たちの「検討した代替案」セクションを加えて書いたものです。
# ADR-0042: 注文サービスにPostgreSQLを使用する
## ステータス
Accepted
## コンテキスト
注文サービスには注文データの永続化ストレージが必要。初期段階で1日50,000件の注文を
処理する見込みで、2年以内に500,000件まで増える。注文の構造は明確に定義されているが、
時間とともにメタデータのフィールドが追加で必要になる可能性がある。チームは
リレーショナルデータベースとドキュメントデータベースの両方の経験がある。
## 決定
注文サービスの主データベースとしてPostgreSQL 15を使用する。
PostgreSQLを選んだ理由:
- 金融に関わる注文データにはACID準拠が必須
- JSONカラムでメタデータのスキーマ柔軟性を確保できる
- インフラチームにPostgreSQLの運用経験がある
- クエリパターンがリレーショナルモデリングに適している
## 検討した代替案
### MongoDB
- 長所: ネイティブなJSONストレージ、水平スケーリングが容易、柔軟なスキーマ進化
- 短所: 一貫性の保証が弱い、チームの運用経験が浅い、
トランザクションのために追加のツールが必要
### DynamoDB
- 長所: フルマネージド、運用負荷が最小、優れたスケーラビリティ
- 短所: AWSへのベンダーロックイン、クエリパターンがパーティション/ソート
キーによるアクセスに限られる、大規模時のコストが予測しにくい
## 結果
### 良い点
- 強力なデータ整合性の保証
- チームが既存のPostgreSQLの専門知識を再利用できる
- 運用特性がよく理解されている
### 悪い点
- スキーマのマイグレーションにはドキュメントストアより計画が必要
- 単一ノードの容量を超えた場合の水平スケーリングが複雑
- スケールの限界に達したらアプリケーションレベルのシャーディングが必要
### リスク
- 注文量が1日100万件を超えたら見直しが必要になる可能性がある
- JSONカラムへのクエリはネイティブなドキュメントストアより効率が悪い
## 関連する決定
- 結果整合性を受け入れる理由はADR-0007を参照
- MongoDBを推奨していたADR-0003を置き換える
コンテキストは具体的に書きましょう。「ACID準拠が必要」のほうが、「信頼性が必要」よりも次の読み手にはるかに役立ちます。そして結果については正直に書きましょう。代償のない決定はありません。欠点を書き留めておけば、将来のチームにいつ見直すべきかが伝わります。
例2:ADR-0007、イベント駆動の通信をMADRで
Kafkaの決定では、検討した選択肢も関わった人も多かったので、MADRの明示的な決定要因と選択肢が役に立ちます。
---
status: accepted
---
# サービス間の非同期通信にKafkaを使用する
## コンテキストと問題設定
マイクロサービスはHTTPで同期的に通信している。そのためサービス同士が密結合になり、
1つのサービスが使えなくなると上流に障害が連鎖する。
呼び出し側がすぐに応答を必要としない場合、サービスはどう通信すべきか?
## 決定要因
* 耐障害性:1つのサービスが落ちても他を巻き込んではならない
* サービス間の結合を緩めること
## 検討した選択肢
* 同期HTTPを維持する
* Apache Kafkaによるイベント駆動の通信
## 決定の結果
選んだ選択肢:「Apache Kafkaによるイベント駆動の通信」。理由は、
連鎖障害の原因となる実行時の結合を取り除けるから。
### 結果
* 良い点:サービスが互いの障害に対してより強くなる
* 悪い点:強い一貫性から結果整合性に移行する
* 悪い点:Kafkaクラスターを運用することになる
* 悪い点:チームにイベント駆動パターンのトレーニングが必要になる
ステータスとタイトルは元の記録から引き継いでいます。元の記録に日付と決定者が残っていなかったため、ここではそのフィールドを省いています。
例3:ADR-0015、フロントエンドのモノレポをY-statementで
モノレポの決定は1文に収まり、その1文で「なぜ全部が1つのリポジトリにあるの?」に十分答えられます。
別々のリポジトリにある5つのフロントエンドアプリケーションのコンテキストにおいて、
バージョンの不一致と、コードを共有するためのパッケージ公開の手間に直面し、
私たちはNxで管理する単一のモノレポを選び
(パッケージ管理を改善した別リポジトリ案と、Nxなしのモノレポ案を退け)、
コード共有を簡単にし、共有コードとそのすべての利用側を1つのプルリクエストで更新できるようにするために、
大きなリポジトリにはビルドキャッシュが必要なことと、アプリケーション同士が意図せず結合しうることを受け入れる。
却下した選択肢の理由は、完全な記録のほうに残っています。別リポジトリでは調整の手間が残り、Nxなしのモノレポではビルド時間が耐えがたいものになっていたでしょう。
どこに置くか
ADRは、それが記述するリポジトリの docs/adr/ か docs/decisions/ に置きましょう(Nygardは doc/arch/adr-NNN.md を提案していました)。コードと一緒にバージョン管理され、コードレビューに登場し、Wikiの中で迷子になることもありません。
番号は連番にし、決して再利用しないでください。決定が変わったら、履歴を書き換えるのではなく、古いADRを置き換える新しいADRを書き、古いほうのステータスを更新します。
ほとんどのチームが飛ばしてしまうのが、ADRを、それが影響するシステムの部分と結びつけるステップです。Archylでは、ADRはNygardのフィールド(ステータス、コンテキスト、決定、結果)に加えて日付と置き換え先へのリンクを持ち、それが形作ったシステム、コンテナ、コンポーネントに紐づけられます。C4ダイアグラムで注文サービスを見ている人は、その隣にADR-0042を見つけます。「なぜこのサービスはPostgreSQLなの?」の答えまで、クリック1つです。
落とし穴(事後にADRを書く、5ページにまで膨らませる、最初の忙しい四半期で習慣をやめてしまう)については、完全ガイドで扱っています。
まずは1つから
PostgreSQLかMongoDBかで2時間続いたあの会議は? 次に同じ問いが持ち上がったとき、誰かがADR-0042を見つけ、10分で終わりました。制約も、代替案も、理由も、すべてそこにありました。元の決定がまだ当てはまることを確認し、次へ進みました。
上のテンプレートから1つを選び、docs/adr/0001-record-architecture-decisions.md に貼り付けましょう。最初のADRは、ADRを書くという決定でも構いません。
アーキテクチャドキュメントについてさらに学ぶ:C4モデルガイド | なぜドキュメントが重要か | ユーザーフローの文書化