【書評】A Philosophy of Software Design 1章-2章

はじめに

本記事はソフトウェアの Complexity (複雑さ) について扱っているA Philosophy of Software Designを読んだ際の個人的な備忘録です。170Pほどの洋書で読みやすいので、読みながら学びになったことをまとめていきます。読みやすいとは言え英語なので和書の5倍くらいの時間がかかりますが、頑張って読んでいきます。

www.amazon.co.jp

1章:イントロダクション

ソフトウェアを開発していくとソフトウェアは次第に複雑になっていき、ソースコードの修正や機能追加が難しくなっていく。結果として機能追加にかかる時間が増大したりバグを引き起こすこととなるが、これはソフトウェアの複雑さに起因すると著者は語る。なんらかのツールによって複雑さを抑えることも可能ではあるが、限界はある。従ってソフトウェアデザインにおいて、ソフトウェアの複雑さを最小にすることで、ソフトウェアをシンプルに保つことが重要である。

よりプログラムをシンプルにするにはどうすればいいか、一般的には以下の二つの手法がある。

  • コードをよりシンプルにすることで複雑さを減らすこと
    • 特殊ケースを消す
    • 統一性のある識別子を利用すること
  • 複雑さをカプセル化し、意識すべき複雑さを減らすこと=moduler design

....とだいたい上記のような内容でした。目から鱗の大発見!!とかはまあなくて、ソフトウェアの複雑性を抑えるにはどうすればいいのだろうかを考える本だよってこと、そしてそれがいかに重要であるかを述べています。introなんで、まあ「ソフトウェアの複雑さを最小化すること」が本書の目的だってわかればまあそれでよしって感じで流し読みしました。次!

2章:複雑さの性質

本書の目的は複雑さを最小化するための原則を知ることにあるわけですが、そもそも複雑さとはなんだろう?2章では高いレイヤーから議論を行っており、より低いレイヤーの議論は後続の章で語られている。まずは敵を知ることから。

Complexity defined | 複雑さの定義

自分が直感的に感じていたことを数式モデルで表現しており、割と良いな!って思った定義が以下です。


C = \sum_{p}c_pt_p \\
C...Complexity of System(ソフトウェアの複雑さ)\\

c_p...part(p) weigit(そのpartの複雑さ)\\

t...developerがそこに触れる頻度や時間 \\

ある関数がとても複雑な処理をしていて、中身がもはやブラックボックスになっていたとします。これはあなたの開発しているソフトウェアの中で複雑さに寄与しているでしょうか? 答えはそこに触れる時間による、です。

普段我々が利用しているライブラリは、モノによっては色々な最適化が施されていたりして、一見するとなんのこっちゃよくわからん複雑なソフトウェアです。例えば僕はtensorflowが裏側でやってくれていることを事細かに把握はしてないですし、知らんけど多分複雑です。そういうソフトウェアをコンポーネントとして扱って開発を行っているわけですが、我々の開発とは隔離されたものと言えるでしょう。どんだけ複雑でも開発から隔離されているものはソフトウェアの複雑さに勘定として入れなくてもいいよね、我々が意識すべき複雑さは開発の中でより長く触れる複雑なコンポーネントですよね。

従って、あるコンポーネントの複雑さを、developerが費やす時間で重み付けしてsumを取ることでソフトウェアの複雑さを測ろう。そういう意図のモデルです。

Symptoms of complexity | 複雑性の症状

複雑さがもたらす症状には大きく分けて3つの症状があります。

Change amplification | 変化の増大

コードを改変する際により多くの箇所を修正する必要が出てくること。本の中で出てくる具体例としてはwebpageの背景色の例が挙げられています。 以下、二つの例を取ってみましょう。

  1. それぞれのページでbackgroundカラーが定義されている
  2. ある一つの変数を経由してそれぞれのページのbackgroundカラーを定義している(変数は一つだけ)

仮に前者のソフトウェアデザインで背景色を変更しようと思うと、全てのページの背景色を変えにいかなければなりません。ページが数千もあったら溜まったもんじゃないです。反して後者ならば変数に代入するパラメータを変えれば全てのページの背景色が切り替わります。複雑なソフトウェアとは、より多くの変更を要求します。

Cognitive load | 認知的負荷

コードを改変するときに必要な情報を整理するのに多大な時間を要するようになる。これはわかりやすい複雑さですね。例えばC言語は認知的負荷が高いです。C言語を書いたことがない人に説明すると驚かれますが、動的配列とかが他の言語のようには提供されてなくて、malloc/freeを使ってメモリ管理を自分でしなければなりません。仮に確保したメモリをfree(開放)し忘れるとメモリリークが起こってメモリが枯渇したりします。こういう要素に気をつけながらコードを書くのは骨が折れるし認知的負荷が高いです。RubyPythonで同等の処理を記述するよりも多くの時間がかかる、と言って否定する人はあまりいないでしょう。

他にも以下のようなことが認知的負荷を引き起こすと指摘されています。

Unknown unknowns: 何を知らないのか分からない

変更対象が分からなかったり、変更に際して何を考慮しなければならないのかがわからないこと。これが症状の中で最悪のものです。苦し紛れに書いたコードが、結果としてどのような問題をもたらすことになるのかもよくわかっておらず、問題が起きてからそれを知ることとなる...知見の溜まってないOSSなどを利用していると起こったりしますね...

Cause of complexity | 複雑さの原因

これまでは複雑さがもたらす症状について触れてきましたが、そもそもなぜソフトウェアが複雑になるのかについて、最後にまとめます。筆者は複雑さの原因を2つに分類しています。

  • dependency | 依存
  • obscurity | 曖昧であること

dependency | 依存

クラス間の依存が増えて行ったり、いろんなところで変数が共通して利用されだすと、途端にコードを追うのが辛くなります。クラスのレイヤーの話でなくとも、他のソフトウェアと依存していることもあります。例えばnetworkのプロトコル。senderの仕様を変更したら、receiverも同様に変更しなければなりません。他にもあるmethodのパラメータを増やした結果、呼び出しもとも同様に変更をしなければならなかったり、rest APIとかもクライアントサイドが仕様を変更するとバックエンドもそれに合わせて仕様を変えなければならなかったり...など、依存しているものが増えるとソフトウェアは複雑になっていきます。こっちは構造のお話

obscurity | 曖昧であること

こっちはコードの可読性というか命名のお話。名が体を表すコードならいざ知らず、よくわからない変数名を定義していて、他の人にはなんのこっちゃよくわからんソースコードは複雑さの原因になります。ソースコードから曖昧さを排除することでソフトウェアはシンプルになります。

1章2章まとめ

後続の章で述べられているDeep Moduleという概念が大変クリアで美しかったため購入した書籍なのですが、この章も個人的には有意義でした。自分が意識していなかったことが述べられているわけではないですが、暗黙知を形式値に昇華してくれたこと、バラバラの知識・経験的直感をまとめ上げて自然言語にしてくれたこと、それだけで大変価値のある文章でした。引き続きのんびり読んでいきます。