アーキテクチャ決定記録(ADR)のベストプラクティス
会議は2時間に及んでいました。新しいサービスにPostgreSQLを使うかMongoDBを使うかを議論していました。リレーショナルの完全性、柔軟なスキーマ、チームの習熟度、運用の複雑さ——議論が飛び交いました。
すると誰かが言いました:「去年、ユーザーサービスでまったく同じ議論をしなかったっけ?」
沈黙。確かにしていました。1年前、同じ質問について同じくらいの時間を費やし、最終的にPostgreSQLを選びました。しかし誰も理由を覚えていませんでした。その議論をリードしたエンジニアは退職していました。そこで私たちは、まったく同じ議論をゼロからやっていたのです。
そのとき、アーキテクチャ決定記録(ADR)を発見しました。以来、解決済みの議論を蒸し返すことにかかる無数の時間を節約してくれています。
ADRとは何か?
ADRは単一のアーキテクチャ決定を記録する短いドキュメントです。システムすべてをカバーする設計ドキュメントではなく、1つの決定だけ:
- 何を決定したか
- なぜそう決定したか
- どんな代替案を検討したか
- 結果はどうなるか
フォーマットは意図的に軽量です。ADRは1ページに収まるべきです。それより長ければ、おそらく複数の決定を記録しています。
良いADRの構造
何十ものADRを書き、何百も読んだ後、最良のものは一貫した構造に従うことがわかりました:
タイトルと番号
すべてのADRに連番と簡潔なタイトルをつけます:
ADR-0042: 注文サービスにPostgreSQLを使用
番号は重要です。決定のタイムラインを作り、議論でADRを参照しやすくします:「ADR-42で決定したように...」
ステータス
ADRにはライフサイクル状態があります:
- 提案中:まだ議論中
- 承認済み:決定済み、これに従う
- 非推奨:もはや関連しない(新しいADRが取って代わった)
- 却下済み:検討したが採用しないことにした
却下済みステータスは特に価値があります。何かを_しなかった_理由を記録したいことがあり、将来のチームが同じことを提案しないようにできます。
コンテキスト
決定を促した状況を説明します。どんな問題を解決しようとしているか?どんな制約があるか?誰が影響を受けるか?
## コンテキスト
注文サービスには注文データの永続化ストレージが必要。初期段階で1日50,000件の注文を
処理し、2年以内に500,000件に成長する見込み。注文は明確に定義された構造を持つが、
時間とともに追加のメタデータフィールドが必要になる可能性がある。チームはリレーショナル
データベースとドキュメントデータベースの両方の経験がある。
制約を具体的に記述してください。「ACID準拠が必要」は「信頼性が必要」よりもはるかに有用です。将来の読者は、決定を形作った力を理解する必要があります。
決定
決定を明確に述べます。「検討するかもしれない」や「探索すべき」ではなく、実際に何を決定したか。
## 決定
注文サービスの主データベースとしてPostgreSQL 15を使用する。
PostgreSQLを選んだ理由:
- 金融的な注文データにはACID準拠が必要
- JSONカラムがメタデータのスキーマ柔軟性を提供
- インフラチームにPostgreSQLの運用経験がある
- クエリパターンがリレーショナルモデリングに適している
これは決定を述べるだけでなく、簡潔に理由を説明しています。次のセクションでより詳細に入りますが、決定セクションだけでも自己説明的であるべきです。
検討した代替案
最も過小評価されているセクションです。選ばなかったものを記録することは、選んだものを記録するのと同じくらい価値があります。
## 検討した代替案
### MongoDB
利点:
- ネイティブJSON storage
- よりシンプルな水平スケーリング
- 柔軟なスキーマ進化
欠点:
- 一貫性保証が弱い
- チームの運用経験が少ない
- トランザクションに追加ツールが必要
### DynamoDB
利点:
- フルマネージド、運用最小限
- 優れたスケーラビリティ特性
欠点:
- AWSへのベンダーロックイン
- クエリパターンがパーティション/ソートキーに限定
- 高スケールでコストが予測不能
新しいメンバーがチームに参加して「なぜMongoDBを使わなかったの?」と聞いたとき、答えがあります。会議をスケジュールしたり、元の決定をした人を探す必要はありません。
結果
すべての決定にはトレードオフがあります。正直に記述しましょう。
## 結果
### ポジティブ
- 強力なデータ整合性保証
- チームが既存のPostgreSQL専門知識を活用できる
- 理解しやすい運用特性
### ネガティブ
- スキーママイグレーションはドキュメントストアより計画が必要
- 単一ノード容量を超えた場合の水平スケーリングがより複雑
- スケール限界に達した場合、アプリケーションレベルのシャーディングが必要
### リスク
- 注文量が1日100万件を超えた場合、再検討が必要かもしれない
- JSONカラムのクエリはネイティブドキュメントストアより効率が低い
このセクションは知的誠実さについてです。完璧な決定はありません。欠点を認めることで信頼を築き、将来のチームが決定を再検討すべきタイミングを理解する助けになります。
いつADRを書くべきか
すべての技術的選択にADRが必要なわけではありません。判断を使いつつ、以下がガイドラインです:
ADRを書くべき場合...
- 決定が複数のチームやサービスに影響する
- 決定を元に戻すコストが高い
- 複数の実行可能なオプションから選んでいる
- 将来のチームメンバーが選択を疑問に思うかもしれない
- 決定が重大な技術的議論を解決する
ADRが不要な場合...
- 選択が明白で異論がない
- 決定が1人または1つのファイルにのみ影響する
- 大きなコストなく簡単に元に戻せる
- チームが常に従っている標準パターンである
例えば:「どのJSONライブラリを使うか?」はおそらくADR不要です。「パブリックAPIにGraphQLとRESTのどちらを使うか?」は間違いなく必要です。
実際のADR例
以下は実際に書いたADRの一部です(この記事向けに簡略化):
ADR-0007: サービス間のイベント駆動通信
コンテキスト:マイクロサービスは現在HTTP経由で同期通信している。これは密結合を生み、サービスが利用不可になるとカスケード障害を引き起こす。
決定:サービス間の非同期通信にApache Kafkaを使用したイベント駆動アーキテクチャを採用する。
結果:
- サービスの障害に対する耐性が向上
- 強い一貫性の代わりに結果整合性
- 運用の複雑さが増加(Kafkaクラスター管理)
- チームにイベントソーシングパターンのトレーニングが必要
ADR-0015: フロントエンドアプリケーションのMonorepo
コンテキスト:5つのフロントエンドアプリケーションが別々のリポジトリにある。コード共有にはパッケージの公開が必要。バージョンの不一致で開発者体験が悪化している。
決定:Nxを使用してすべてのフロントエンドアプリケーションを単一のmonorepoに統合する。
却下した代替案:
- より良いパッケージ管理で別々のリポジトリを維持:調整のオーバーヘッドが残るため却下
- Nxなしの単一リポジトリ:ビルド時間が問題になるため却下
結果:
- コード共有の簡素化と一貫性
- 単一のPRで共有コードとすべてのコンシューマーを更新可能
- より大きなリポジトリにはビルドキャッシュのためのより良いツールが必要
- アプリケーション間の意図しないカップリングの可能性
よくある間違い
間違い1:事後にADRを書く
ADRを書く最良のタイミングは決定プロセスの最中であり、数週間後ではありません。後から書くと、ニュアンス、検討した代替案、具体的な制約を忘れてしまいます。
今では決定プロセスの一部としてADRのドラフトを書いています。議論はADRドキュメント内で行われ、消えるSlackスレッドではありません。
間違い2:長すぎる
ADRが1ページを超える場合、おそらく:
- 複数の決定を記録している(複数のADRに分割)
- 実装の詳細を含んでいる(設計ドキュメントに任せる)
- 明白なコンテキストを過剰に説明している
簡潔さの規律が明快さを生みます。
間違い3:関連ADRをリンクしない
決定が孤立して存在することはめったにありません。イベント駆動通信にKafkaを選んだとき(ADR-0007)、それはデータベースの選択(ADR-0042)に影響しました。結果整合性を受け入れられるようになったからです。
関連ADRを相互参照しましょう:
## 関連する決定
- 結果整合性を受け入れる理由はADR-0007を参照
- MongoDBを推奨していたADR-0003を置き換え
間違い4:実践を放棄する
ADRは時間とともに価値を提供します。1〜2個のADRはあまり役に立ちません。何年もかけて蓄積された50以上のADRのコーパスは、非常に価値があります。アーキテクチャの進化の検索可能な歴史です。
忙しくなると書くのをやめたくなります。それに抵抗してください。ADRを書く10分が、後で数時間の会議を節約します。
ADRをワークフローの一部にする
最も難しいのはADRを書くことではなく、習慣にすることです。うまくいく方法を紹介します:
コードと一緒に保存する
ADRをリポジトリに、通常はdocs/adr/やdocs/decisions/に置きます。これにより:
- 記述するコードと一緒にバージョン管理される
- コードレビューに表示される
- どこかのwikiで孤立することがない
テンプレート化する
みんなが使うmarkdownテンプレートを作成します。これにより摩擦が減り、一貫性が確保されます:
# ADR-NNNN: タイトル
## ステータス
提案中 | 承認済み | 非推奨 | 却下済み
## コンテキスト
[この決定を促している問題は何か?]
## 決定
[提案および/または実行する変更は何か?]
## 検討した代替案
[他にどのようなオプションを検討したか?]
## 結果
[この変更により何が容易になり、何が困難になるか?]
四半期ごとにADRをレビューする
四半期ごとにADRをレビューするカレンダーリマインダーを設定します:
- もはや関連しない決定はあるか?
- 前提を無効にする状況変化はあったか?
- 記録すべき未記録の決定はあるか?
ADRをアーキテクチャ図にリンクする
ここがArchylのようなツールが輝くところです。ADRをC4図の特定のコンポーネントにリンクすると、そのコンポーネントを見る誰もが、それを形作った決定を即座に確認できます。
「なぜこのサービスはデータベースに直接ではなくKafkaと通信しているのか?」リンクされたADRをクリックすれば答えがわかります。
まとめ
アーキテクチャ決定記録は、必要になるまではオーバーヘッドのように思えるプラクティスの一つです。必要になったとき、それは計り知れない価値を持ちます。
PostgreSQL対MongoDBのあの2時間の会議?誰かが元のADRを見つけてから10分で終わりました。すべてのコンテキストがそこにありました——制約、代替案、推論。元の決定がまだ有効であることをすぐに確認し、先に進みました。
小さく始めましょう。次にアーキテクチャの決定をするとき、15分かけてADRを書いてください。リポジトリに入れてください。6ヶ月後、誰かが「なぜそうしたの?」と聞いたとき、答えがあります。
アーキテクチャドキュメントについてさらに学ぶ:C4モデル入門 | なぜドキュメントが重要か | ユーザーフローの文書化