TL;DR
- 複雑さとは、システムの理解や変更を難しくする、ソフトウェアの構造に関わるあらゆるものである。
- 複雑さは変更の増幅、認知的負荷、未知の未知として表れ、依存関係と不明瞭さが少しずつ積み重なって生まれる。
内容
複雑さとは
システムの複雑さとは、システムの理解や変更を難しくする、ソフトウェアの構造に関わるあらゆるものを指す。たとえば、コードの一部がどう動くのか理解できない、ちょっとした変更にも大きなコストがかかる、どこを直せばよいか分からない、別のバグを生まずにバグを直すのが難しい、といった状態である。理解や修正が難しければ複雑で、簡単であれば単純である。
ただし、非常に複雑な部分があっても、そこに触れる機会がほとんどなければ、システム全体の複雑さにはほとんど影響しない。システム全体の複雑さ は、各部分 の複雑さ を、その部分に費やす時間 で重み付けした和で表せる。
つまり、複雑さを誰も触らない場所に隔離することは、複雑さを完全になくすのと同じである。
また、複雑さは書き手よりも読み手のほうが気づきやすい。自分だけが作業しやすいコードではなく、他の人も作業しやすいコードを書くべきである。
複雑さの 3 つの症状
複雑さは 3 つの形で表れる。
1 つ目は変更の増幅(change amplification)である。一見単純な変更でも、多くの場所でコードを直す必要が生じる。
2 つ目は認知的負荷(cognitive load)である。タスクを終えるために、開発者が知っておくべき知識の量を指す。認知的負荷が高いと、必要な情報を得るのに時間がかかり、重要なことを見落としてバグを生むリスクも高まる。認知的負荷は、モジュール間の依存関係やグローバル変数など、さまざまな形で生まれる。複雑さはコードの行数では測れない。行数が多いアプローチのほうが認知的負荷を下げ、結果としてシンプルになることもある。
3 つ目は未知の未知(unknown unknowns)である。タスクを終えるためにどのコードを直すべきか、どんな情報が足りないのかが分からない状態を指す。
優れた設計の最も重要な目標の 1 つは、システムが明白であることである。これは、認知的負荷が高い状態や未知の未知がある状態とは正反対である(第18章 Code Should Be Obvious)。
複雑さの原因
依存関係とは、あるコードを単独では理解も修正もできない状態を指す。そのコードは他のコードと何らかの形で関係しているので、変更するときは他のコードも考慮したり変更したりする必要がある。たとえば、クライアントとサーバの関係では、送信側のクライアントのコードを変えると、受信側のサーバのコードも変える必要がある。依存関係は完全にはなくせない。そのため、ソフトウェア設計では依存関係の数を減らし、残った依存関係をできるだけ単純で明白にすることを目指す。依存関係は、変更の増幅と認知的負荷をもたらす。
不明瞭さとは、重要な情報が明白でない状態を指す。変数名が一般的すぎて情報を持っていない、変数名に単位が書かれていない、といった例がある。不明瞭さはドキュメントの不足から生じるが、その根には設計上の問題があることが多い。システムが明白であれば、必要なドキュメントは少なくて済む。逆に、必要なドキュメントが多いということは、設計が適切でない。不明瞭さは、認知的負荷と未知の未知をもたらす。
依存関係と不明瞭さを取り除けば、複雑さを減らせる。
複雑さは少しずつ増える
複雑さは、小さな依存関係や不明瞭さが積み重なって生まれる。一度たまった複雑さを取り除くのは難しく、1 つ解消しただけでは全体は良くならない。複数の開発者がこうした小さな問題を見過ごすと、複雑さは急速に増える。そのため、小さな複雑さも許さないゼロ・トレランスの姿勢が必要になる(第3章 Working Code Isn't Enough)。
複雑になるほど、既存のコードを変更するのは難しくなり、リスクも高くなる。
キーワード
自分の理解
3 つの症状の危険度は、変更の増幅、認知的負荷、未知の未知の順に高くなると考えている。
- 変更の増幅は、変更する箇所が明確なので、まだ変更できる
- 認知的負荷は、どの情報を読めばよいかが明確なので、まだ変更できる
- 未知の未知は最悪で、システムのコードを全行読むしかない