A Philosophy of Software Designから学ぶComplexity

# 概要
名著だと言われているけど読んだことがなかった本の
[A Philosophy of Software Design](https://www.amazon.co.jp/Philosophy-Software-Design-John-Ousterhout/dp/1732102201)
を暇な時間に読んだので、忘れないうちに印象に残った箇所をメモとして投稿しておく。

自分なりの理解を含めてメモを書いているので、誤っている部分がある場合にはご指摘いただけると喜びます。

# The Nature of Complexity 

## Complexity defined

複雑さへの対策を考えるよりも先に、ソフトウェアにおける複雑さとは何かを定義する必要がある。
この本での複雑さとは、**ソフトウェアの理解と変更を難しくする、ソフトウェアの構造に関係するもの**のこと。

複雑さは多様な形を持っている。
例を挙げるならば、どのように動くか理解が難しいコードかもしれないし、改良を加えるためにソフトウェアの何を修正すべきかが分からない箇所かもしれない。
もしソフトウェアの理解と変更が難しいなら、それは複雑であり、その逆であればシンプルだ。

コストとメリットの観点から複雑さを考えることもできる。
複雑さを伴うソフトウェアにおいては、わずかな改良ですら多くの実装を行う必要がある。
対してシンプルなソフトウェアにおいては少ない労力でより大きな改良を実装することができる。


複雑さは必ずしもソフトウェア全体の大きさや機能とは関連するものではない。
よくComplexという言葉を洗練された機能を持つ大きなソフトウェアを表現する時に使うが、もしそのようなソフトウェアで作業をするのが簡単なら、この本の定義という観点から言えばComplexではないと言える。

複雑さはそのソフトウェアにおいて最も一般的なアクティビティによって決まる。仮にソフトウェアに非常に複雑な部分がいくつかあったとしても、それらの部分にほとんど触れる必要がない場合にはそのソフトウェア全体の複雑さには大きな影響を及ぼさない。

著者はこの法則を以下のような数式で表している。

```math
C = \sum_{p} c_{p}t_{p}
```

この数式はソフトウェア全体の複雑さ(C)は各部分の複雑さ(cp)と開発者がその部分の作業に費やす時間(tp)によって決まるとしている。


## Symptoms of complexity(複雑性の症状)

一般的に、複雑さは次の3つの方法で現れる。

1\.Change Amplification(変更の増幅)
2\.Cognitive load(認知的負荷)
3\.Unknown unknowns(未知の未知数)


### 1.Change Amplification(変更の増幅)

Change Amplification(変更の増幅)とは、**一見、単純に見えるコードの修正があらゆる場所で発生すること。**
例えば、Webサイトの背景色を管理する時に一つの変数を管理用のパラメータとして持つのではなく、各ページでパラメータを定義したとき、この現象が発生する。
このような単純な変更でも、Webサイトが巨大で、数百、数千単位で変更するページが必要であれば修正する際に漏れが生じる可能性が高い。


### 2.Cognitive load(認知的負荷)

Cognitive load(認知的負荷)とは、**開発者がタスクを完了するためにどれだけの情報を知るべきかを指す。**
Cognitive load(認知的負荷)が高いと、開発者が必要な情報を得るためにより多くの時間を費やす必要があり、重要なことを見逃すことが多くなる。結果として、バグのリスクが高まる。

ソフトウェア設計者は複雑さをコード行数で測定できると思い込んでいることがある。
しかし、短いが何をしているかわからないコードより、長くても読んですぐに何をしているかが分かるコードの方が単純なアプローチは認知的負荷を軽減するため、複雑さには影響を与えない。

### 3.Unknown unknowns(未知の未知数)

Unknown unknowns(未知の未知数)とは、**タスクを完了するためにどのコードを変更する必要があるか、または開発者がタスクを完了させるために必要な情報が未知数であること。**

例えば、1.Change Amplification(変更の増幅)を解消するために、Webサイト内の背景色を一つの変数で管理しているとする。
しかし、そのWebサイトは実は特定の数ページにおいてのみ背景色を強調する仕組みを組み込んでいて、それぞれのページで管理している場合、開発者はこれに気付かない場合が多い。
仮に気付いたとしても、修正時に全てのページを網羅できる可能性は高くない。

これまで挙げてきた3つのうち、Unknown unknowns(未知の未知数)は最悪で、知っておくべき情報を知る方法がないということだ。
つまり、変更を加えた後にバグが発生するまでバグの存在を知ることはできない、ということになる。

優れた設計の最も重要な目標の一つは、ソフトウェアが明確であることだ。
これは高いCognitive load(認知的負荷)とUnknown unknowns(未知の未知数)の真逆であり、明白なソフトウェアとは開発者が深く考えなくても何をすべきかを素早く推測でき、その推測が正しいと確信を持てるソフトウェアである。

## Causes of complexity

複雑さは依存関係と曖昧さによって引き起こされる。

依存関係が存在するのは特定のコードを単独で理解、および変更できない場合だ。
依存関係はソフトウェアの基本的な部分であり、完全に排除することはできない。実際、ソフトウェア設計プロセスの一部として依存関係を意図的に導入している。新しいクラスを記述するたびにそのクラスのAPIに関する依存関係を作成する。
ただし、ソフトウェア設計の目標の1つは依存関係の数を減らし、依存関係を可能な限り単純で分かりやすいものにすることだ。

曖昧さは重要な情報が明確でない場合に発生する。
例えば、汎用的すぎて何を持っているかが明確でない変数名や、単位が指定されていない状態で使用されている変数などだ。
また、一貫性のなさが曖昧さの原因であることもある。同じ変数名が2つの異なる目的に使用されている場合、開発者には使用されている変数がどちらの目的に向けて使うべきかが分からないということだ。
多くの場合、曖昧さはドキュメントによる説明の不足が原因で発生するが、一方で、曖昧さも設計上の問題であると言える。
仮にソフトウェアの設計がクリーンで、明白な場合には必要なドキュメントの量は少なくなる。

大量にドキュメントを用意する必要がある場合には設計が正しくない可能性が高い。


## Complexity is incremental

複雑さは一つの致命的なエラーではなく、何百、何千という小さな依存関係や曖昧さが時間と共に蓄積され、発生する。
これらの小さな問題が非常に多く存在するので、ソフトウェアに対する全ての変更がそれらにいくつかの影響を与えることになる。複雑さは漸進的なので、徐々に制御が難しくなる。

今持っているタスクを消化するために行った修正で発生した多少の複雑さは大したことないと自分に言い聞かせるのは簡単だが、全ての開発者がこの考え方を採用すると複雑さは一気に蓄積される。
そして、いったん複雑さが蓄積されると、単一の依存関係や曖昧さを取り除いてもそれだけでは大きな違いを生まない(=改善)ため、複雑さを排除することは困難だ。



# 感想

アメリカの現場でも日本と似たようなことは起こっているんだな、という点が意外でした。
全編英語であるため、買う時は少し身構えていましたが、そこまで理解の難しいような文章が出てくるわけでもなく、分からない単語が出てきた時に辞書を引くくらいで何とかなるレベルだったので非常に助かりました。
Kindle版であれば1000円くらいで買えるのでとりあえず積読するのにもおすすめです。