/note/tech

エンジニアの脳が壊れる瞬間 ─ 複雑性・認知負荷・計算量のメカニズム

要約:

■ 1. はじめに:複雑性が問題を生む理由

  • ソフトウェア開発には「複雑性が問題を生む」という常識がある
  • 問い:
    • なぜ複雑になると破綻するのか
    • どこから複雑と呼べるのか
    • どの瞬間に人の理解が追いつかなくなるのか
    • 技術負債はなぜ突然「手がつけられない」段階まで膨張するのか
  • ソフトウェアの複雑性を計算量、認知負荷、チーム特性という3つのレンズで読み解く
  • ポイントは「複雑なコード」ではなく「複雑さ自体の本質」を扱うこと

■ 2. 複雑性とは計算量の爆発を人間が受け止められなくなる現象

  • ソフトウェアの複雑性はしばしば計算量の爆発として語られる:
    • O(N) → まだ追える
    • O(N²) → 何が起きているか曖昧になる
    • O(2^N) → もはや理解不能
  • しかしこれは人間の脳にもまったく同じことが起きる
  • 人間の作業記憶は7±2個しか保持できない(心理学の古典的研究)
  • これはエンジニアにとっては「認知の計算量制限」を意味する
  • その後の研究では実効容量は4±1個程度という見解も有力(Cowan 2001など)
  • つまりコード設計がO(理解)を超えた瞬間、人は壊れる
  • 人間の作業記憶は5〜9チャンク程度しか同時に扱えないとされており、複雑な設計はすぐにこの上限にぶつかる

■ 3. 設計の計算量はコード行数ではなく関係性の数で決まる

  • 複雑性はコード量では決まらない
  • 関係性(依存・制約・フロー)の数で決まる
  • 例:
    • AがBを知っていて
    • BがCを参照していて
    • Cが非同期にAを更新する
    • これを理解するには人間はA-B-C-Aの閉路を追跡しなければならない
    • これは脳にとってDFS(深さ優先探索)のような負荷
  • 関係性が増えるほど理解の計算量は指数的に増える
  • コードは線形に増えるが理解コストは指数関数的に増える
  • だから設計は突然破綻する
  • 崩壊は徐々には進まない
  • ある閾値を超えた瞬間いきなり崩れる
  • まるで「臨界点」を超えた化学反応のよう
  • 依存関係の数が増えると開発スピードより先に「依存の管理コスト」が爆発していく

■ 4. 認知負荷は見えない技術負債である

  • 複雑性が破綻するのは技術の問題ではなく人間が処理できる情報量の限界を超えるため
  • 認知負荷が高い設計には次の兆候がある:
    • 一度読んでも理解できない
    • 仕様とコードの対応が不明
    • 「ここを変えるとどこに影響する?」が見えない
    • レビューが摩耗する
    • バグが「点」ではなく「面」で発生する
  • これらはすべて「計算量過多」「キャパシティ超過」の症状
  • 技術負債とは「認知負荷が金利として積もっていく現象」と捉えるべき
  • 技術負債は単なる「後回しの修正」ではなく開発者の頭の中に積み上がる認知負荷そのもの

■ 5. アーキテクチャの本質は計算量の制御

  • 設計とは格好いい図を描くことではない
  • 本質はただ一つ:「理解に必要な計算量をO(1)に近づけること」
  • そのためにアーキテクチャが存在する:
    • 層で区切る → 認知対象を一定に保つ
    • 責務を限定する → 計算経路を短くする
    • 境界を作る → 関係性を切断する
    • APIを設計する → 認知を抽象化する
  • きれいな設計にも理由がある
  • それは美しいからではなく脳への負荷を最小化するため

■ 6. 複雑性の破綻ラインは個人ではなくチームに依存する

  • 認知負荷は人によって異なる:
    • 初心者はすぐO(爆発)
    • ベテランはO(ある程度耐える)
    • 設計者はO(ノイズを抽象化できる)
  • つまり複雑性の破綻ラインはチーム固有のものであり汎用的な「正しさ」では語れない
  • 技術は移植できても認知限界は移植できない
  • あるプロジェクトでうまくいった設計が別のチームでは壊滅する理由はここにある

■ 7. 実務で複雑性の爆発を防ぐ方法

  • (1) 関係性を減らす(依存を切る):
    • イベント駆動で発散した依存を整理する
    • 双方向参照を禁止する
    • 「AはBを知らない世界」を設計する
    • 短い例:UI→UseCase→Repositoryだけにし、UIがRepositoryを直接呼ばないようにする
  • (2) 読み方をO(1)に近づける:
    • コードが「初見で」理解できる必要がある
    • 初回で理解できない設計は複雑すぎる
    • 短い例:「この関数は何をするか?」が1画面以内で完結するようにする
  • (3) ドキュメントではなく構造で説明する:
    • ドキュメントで補強が必要な設計はすでにO(1)ではない
    • 短い例:「ここは○○層です」と説明不要なようにフォルダと責務を一致させる
  • (4) レビューで認知負荷を観測する:
    • レビューの摩耗は構造の疲労
    • 短い例:PRに「ここ分かりづらい」コメントが連発したら設計の構造疲労を疑う
  • (5) チームの認知限界を把握する:
    • ベテランの限界ではなくチームの最低ラインで設計すべき
    • 短い例:「新人でも追える構造」を基準にしベテラン依存の暗黙知を排除する

■ 8. おわりに

  • 複雑性の問題は理論では説明できるが実務ではほとんど語られない
  • しかしソフトウェアが破綻するのはいつだって「人間が理解できなくなったとき」
  • ソフトウェアの限界は計算資源ではなく人間の認知資源で決まる
  • この視点が定着すると設計やレビューやアーキテクチャの見え方が変わる
  • 複雑性を避けるのではなく複雑性と「共存できる形」を探すことが大切