TL;DR

  • 複雑さとは、システムの理解や変更を難しくする、ソフトウェアの構造に関わるあらゆるものである。
  • 複雑さは変更の増幅、認知的負荷、未知の未知として表れ、依存関係と不明瞭さが少しずつ積み重なって生まれる。

内容

複雑さとは

システムの複雑さとは、システムの理解や変更を難しくする、ソフトウェアの構造に関わるあらゆるものを指す。たとえば、コードの一部がどう動くのか理解できない、ちょっとした変更にも大きなコストがかかる、どこを直せばよいか分からない、別のバグを生まずにバグを直すのが難しい、といった状態である。理解や修正が難しければ複雑で、簡単であれば単純である。

ただし、非常に複雑な部分があっても、そこに触れる機会がほとんどなければ、システム全体の複雑さにはほとんど影響しない。システム全体の複雑さ は、各部分 の複雑さ を、その部分に費やす時間 で重み付けした和で表せる。

つまり、複雑さを誰も触らない場所に隔離することは、複雑さを完全になくすのと同じである。

また、複雑さは書き手よりも読み手のほうが気づきやすい。自分だけが作業しやすいコードではなく、他の人も作業しやすいコードを書くべきである。

複雑さの 3 つの症状

複雑さは 3 つの形で表れる。

1 つ目は変更の増幅(change amplification)である。一見単純な変更でも、多くの場所でコードを直す必要が生じる。

2 つ目は認知的負荷(cognitive load)である。タスクを終えるために、開発者が知っておくべき知識の量を指す。認知的負荷が高いと、必要な情報を得るのに時間がかかり、重要なことを見落としてバグを生むリスクも高まる。認知的負荷は、モジュール間の依存関係やグローバル変数など、さまざまな形で生まれる。複雑さはコードの行数では測れない。行数が多いアプローチのほうが認知的負荷を下げ、結果としてシンプルになることもある。

3 つ目は未知の未知(unknown unknowns)である。タスクを終えるためにどのコードを直すべきか、どんな情報が足りないのかが分からない状態を指す。

優れた設計の最も重要な目標の 1 つは、システムが明白であることである。これは、認知的負荷が高い状態や未知の未知がある状態とは正反対である(第18章 Code Should Be Obvious)。

複雑さの原因

複雑さの原因は、依存関係と不明瞭さの 2 つである。

依存関係とは、あるコードを単独では理解も修正もできない状態を指す。そのコードは他のコードと何らかの形で関係しているので、変更するときは他のコードも考慮したり変更したりする必要がある。たとえば、クライアントとサーバの関係では、送信側のクライアントのコードを変えると、受信側のサーバのコードも変える必要がある。依存関係は完全にはなくせない。そのため、ソフトウェア設計では依存関係の数を減らし、残った依存関係をできるだけ単純で明白にすることを目指す。依存関係は、変更の増幅と認知的負荷をもたらす。

不明瞭さとは、重要な情報が明白でない状態を指す。変数名が一般的すぎて情報を持っていない、変数名に単位が書かれていない、といった例がある。不明瞭さはドキュメントの不足から生じるが、その根には設計上の問題があることが多い。システムが明白であれば、必要なドキュメントは少なくて済む。逆に、必要なドキュメントが多いということは、設計が適切でない。不明瞭さは、認知的負荷と未知の未知をもたらす。

依存関係と不明瞭さを取り除けば、複雑さを減らせる。

複雑さは少しずつ増える

複雑さは、小さな依存関係や不明瞭さが積み重なって生まれる。一度たまった複雑さを取り除くのは難しく、1 つ解消しただけでは全体は良くならない。複数の開発者がこうした小さな問題を見過ごすと、複雑さは急速に増える。そのため、小さな複雑さも許さないゼロ・トレランスの姿勢が必要になる(第3章 Working Code Isn't Enough)。

複雑になるほど、既存のコードを変更するのは難しくなり、リスクも高くなる。

キーワード

自分の理解

3 つの症状の危険度は、変更の増幅、認知的負荷、未知の未知の順に高くなると考えている。

  • 変更の増幅は、変更する箇所が明確なので、まだ変更できる
  • 認知的負荷は、どの情報を読めばよいかが明確なので、まだ変更できる
  • 未知の未知は最悪で、システムのコードを全行読むしかない