はじめに
長く運用されているシステムを読み解くとき、しばしば「これは本当に必要な複雑さなのか、それとも何かの副産物なのか」という問いに行き当たる。ソフトウェアの複雑性は**本質的複雑性(essential complexity)と偶有的複雑性(accidental complexity)**に分けて考えられる。前者は問題そのものに由来し、どう実装しても避けられない複雑さ。後者は実装手段や歴史的経緯から滲み出ているだけの複雑さで、原理的には消せる。
これら2つには、簡単に見分けがつくものもあれば、コードや仕様だけを見ても判別が難しいものもある。本稿では、偶有的複雑性が生まれる典型的な要因を整理しつつ、「意思決定の記録が残っていないこと」自体が、偶有と本質を切り分けにくくする要因のひとつであるという話をしたい。
偶有的複雑性はどこから生まれるか
偶有的複雑性は単一の発生源を持たない。**「何に起因しているか」**という原因軸で見ると、おおむね次の3つに分けられる。
業務要件・運用都合由来
「業務的にそう決めた」だけのもの。ステークホルダーや当時の運用都合に合わせて作られたが、必ずしも普遍的な要件ではない。
- ある画面でだけ特殊な権限チェックが入っている
- 集計ロジックが「全体の最小値」「直近30日の合計」のような、特定組織のオペレーション向け粒度になっている
- 機能Aと機能Bが連鎖して動く(片方だけ使うことが想定されていない)
これらは新しいアーキテクチャに移っても、ステークホルダーと再合意しない限り消えない。技術的には「再設計可能」だが、人間側の合意で初めて消えるのが特徴。
技術選択・実装手段由来
選んだフレームワーク・ミドルウェア・プロトコルの制約から滲み出るもの。よくある例は次のようなもの。
- 認証Cookieのドメイン制約に合わせて、サブドメインごとに別セッションを持たざるを得ない構造
- ORMの制約で「論理削除カラム + 一意制約」の組み合わせが歪んでいる
- 単一プロセス前提のフレームワークに合わせて、ジョブキューが過剰に補完的役割を担っている
これらはアーキテクチャ選択を変えれば構造ごと消える可能性が高い。別プロトコルに寄せる、別ミドルウェアを採用する、別言語で書き直す、といった方向転換で「そもそもこの問題が存在しなくなる」ことがある。
組織・時間制約由来
業務要件から生じたものではない。技術制約から生じたものでもない。**「当時はそう書くしかなかった」**系の偶有がここに該当する。納期・人員・組織体制・歴史的経緯から滲み出るもので、3つの中ではいちばん見落とされやすい。
- 当時のチームに該当領域の知見がなく、安全側に倒した実装が残っている
- 別チーム所有のコードに手を入れられず、自分の側で迂回ロジックを抱え込んでいる
- リリース直前の差し込みで作られた一時しのぎが、そのまま恒久実装になっている
- 「2つの組織が合流したときに、両方の概念が並存したまま残った」型のデータモデル
これらは業務要件のように見えることもあれば、技術制約のように見えることもある。だが本当の出所はその時点の組織と時間の都合であって、技術選択を変えても業務要件を再合意しても消えない。消すには**「当時の制約はもう存在しないので、書き直す」**という意思決定が要る。
なお、UX上の歪み(毎回ログインを求められる / 二重通知が出る / 操作の途中で文脈が切れる など)は、これら3つの観察結果として現れる現象であって、独立した原因ではない。原因軸を直すとUXも同時に直ることが多いので、本稿では原因の方に絞っている。
なぜ偶有と本質の切り分けは難しいのか
「本質か偶有か」は、コードを読んだだけでは決まらない。なぜそうなっているかを知らないと、目の前の構造が「業務上どうしてもこうなる」のか「当時のフレームワーク制約に合わせただけ」なのかが判別できない。
つまり、切り分けには当時の意思決定の文脈が必要になる。具体的には次のような情報だ。
- どんな選択肢があったのか
- なぜその選択肢を採用したのか(性能、工期、既存資産との互換、採用容易性など)
- 何を制約として受け入れたのか
- 何を「将来見直す可能性あり」として保留したのか
これらが残っていれば、新しい設計者は「この複雑さは当時の制約Xに由来する。Xが消えた今、構造ごと捨ててよい」と判断できる。逆に残っていなければ、すべての構造が「何か理由があるはず」というブラックボックスとして扱われ、安全側に倒して温存される。
意思決定の記録不在が生む3つの問題
偶有が「本質」に格上げされる
最も典型的な失敗は、偶有的な構造が時間とともに本質的要件として扱われ始めることだ。
「これは仕様です」
と説明されている挙動の多くは、実際には初期実装時の制約に合わせた偶有でしかない。だが記録がないと反証できないため、新システムを作るときも「現行と同じ挙動を維持してください」という形で要件化されてしまう。結果として、次の世代のシステムも同じ偶有を引き継ぐ。
「触ってはいけない」の範囲が膨張する
意思決定の記録がないと、コードに対して**「なぜそうなっているか分からないが、変えると壊れそう」**という心理的な壁が積み重なる。これはChesterton's Fence(チェスタートンの柵)の問題に近い。柵が立っている理由が分からない以上、安全側に倒して残すしかない。
短期的には合理的だが、これを5年・10年続けると、システム全体が「触ってはいけない箇所」で覆われ、リファクタリング可能な領域が消滅していく。
議論が「現行踏襲かどうか」に収束する
新システムを設計する場面では、「現行の挙動を再現する/しない」という二択が論点の中心になりがちだ。本来議論すべきなのは「業務上どんな性質を保証したいか」だが、その問いに答えるための一次資料(当時の意思決定)が無いと、現行の挙動を真似することが最も安全な選択になってしまう。
結果、新システムは**「現行の偶有を全部含んだまま、技術スタックだけ新しくなったもの」**に近づく。これでは作り直す意味が薄い。
ADR / Design Docは「未来の自分への補助線」
この問題に特効薬はない。だが、設計判断のたびにその時点での選択肢・採用理由・受け入れた制約をADRやDesign Docとして残しておくことは、最も費用対効果の高い対策のひとつだろう。
ADRやDesign Docは「正解を残す」ものではない。むしろ次のような価値がある。
- 偶有を偶有として可視化する:「この構造はXという制約に合わせた」と書いておけば、Xが消えた将来、その構造を捨てる判断材料になる。
- 本質を本質として固定する:「この要件は業務上譲れない」と記録があれば、新システムでも引き継ぐべき性質として扱える。
- 保留した選択肢を未来に渡す:「今回は採用しなかったが、状況が変わったら再検討」と書いておけば、後の世代が再評価できる。
逆に言えば、ADRやDesign Docを書かないということは、未来の自分(あるいは後任)から偶有と本質を切り分ける手段を奪っていることに近い。
では、どこまで書くべきか
「全ての判断を記録するべき」という主張は実務的でない。ADRやDesign Docを書くこと自体にコストがかかるし、書かれたドキュメントを保守するコストもある。書く対象は次のようなものに絞ってよいだろう。
- 後から「なぜこうなっているか」と問われそうな判断(特に、複数の選択肢の中から非自明なものを選んだとき)
- 業務要件と技術制約のどちらに由来するかが曖昧な判断
- 将来の前提変化(フレームワーク刷新・スケール変化・組織変化)で見直す可能性のある判断
逆に、明らかにベストプラクティスに従っただけの判断や、誰がやっても同じになる判断は、わざわざADRにしなくてもよい。**「書くべきは判断であって、結果ではない」**が原則だろう。
まとめ
- ソフトウェアの複雑性は本質と偶有に分けられるが、簡単に見分けがつくものとそうでないものがある。
- 偶有は「業務要件・運用都合 / 技術選択・実装手段 / 組織・時間制約」の3つの原因から生まれ、それぞれ消える条件が異なる。
- 切り分けには当時の意思決定の文脈が必要だが、それが残っていないことが多い。
- 結果として偶有が本質に格上げされ、新システムにも引き継がれてしまう。
- ADRやDesign Docは正解を残すものではなく、偶有を偶有として、本質を本質として後世に伝えるための補助線として効く。