プロのプログラマとして働く方に、是非とも呼んでほしいと思ったのがこの本だ。
自分はこの本に出会い、コードを書くということへの向き合い方が大きく変化した。
今年読んだ本の中で、一番仕事に活かせた本と言い切ることが出来る。
もしあなたがプログラマであり、よりよいプログラマを目指しているなら、この本を読む理由はそれだけで十分と思う。
参考文献
Clean Code アジャイルソフトウェア達人の技 (アスキードワンゴ)
Robert C.Martin (著), 花井 志生 (著)
何故コードは美しくしなければならないのか
コードは動けばいいじゃないか、とまではいかなくとも、納期の短いプロジェクトでは時間をかけてコードをより美しくすること、ましてやコードを美しくする為だけに、動いているプログラムにわざわざ手をいれることは嫌煙されがちだ。
しかし、本書によるとクリーンでないコード=粗悪なコードは、開発の生産性をどんどん低下させていき、いずれ0にしてしまうとある。
将来に渡る生産性を低下させないためにも、コードは常にきれいな状態に保つ必要がある。
クリーンなコードは、つまり読みやすく、理解しやすく、テストがしやすいものだ。
これは長期的なコストが一番安くなることに繋がる。
当然ながら、コードは書かれるよりも読まれることの方がずっと多い(10:1以上だ)
であれば、今多少書くのが大変でも、将来の開発者(または自分)が、より読みやすいコードを維持しておくことが、重要であるといえる。
「最初よりも美しく」 ~ボーイスカウトルールの是非について~
現場で意見が分かれるのがこれだ。
ボーイスカウトにある規則
「キャンプ場を、自分が来た時よりもきれいな状態にすること」
これをソフトウェア開発に置き換えて
コードに手を入れる前よりも、周辺を綺麗にして(つまりリファクタリングして)コミットする。
これは、日本の開発現場に長くはびこる
「動いているプログラムは触るな」と真っ向からぶつかる考え方だ。
もし現場がこちらの考え方寄りであるなら、後述する単体テストを整備して、リファクタリングが可能な土壌を用意していく必要がある。
「命名」~読みやすいプログラムの一丁目一番地~
この本に限らず、より読みやすいプログラム、保守性の高いコードについて書かれている本では、必ず一丁目一番地に出てくるのがこの「命名」だ。
変数や関数、引数、クラスやパッケージ、ファイルやディレクトリに付ける"名前"について。それらの名前の付け方が、粗悪なコードかクリーンなコードかを大きく分ける。
この「命名」は、ソフトウェア開発の基本ではあるが、つい疎かになりがちだ。
変数にしろ、関数名にしろ、名前をつけるうえで重要なポイントを本章からまとめた。
- 意図が明確な名前にする
⇒「なぜそれが存在するのか?何をするのか?どのように使用するのか?」が名前だけで伝わらなければならない。 - ハンガリアン記法やメンバープレフィックスは不要
⇒型情報の明示化や、クラスのメンバであることを明示化する目的で付けられるこれらの情報はノイズになる。型が変わった際にリネームの必要が出てくるという問題もある。
コードの読み手に、変数や関数の名前の意味にだけ集中させるということで、これらを変数名に付けることは推奨しないとある。
(残念ながら、私の現場ではどうしてもハンガリアン記法が染みついており、やめるに至っていない) - クラス名には、Customer、WikiPage、Accountなどの名詞、または名詞句を付けるべき。
⇒クラスは動作ではなく概念であるべきなので、動詞をつけるべきではない。また名詞であってもManager、Processorなどは広義過ぎて、その責任範囲が名前からは分からないのでNG
DataやInfoもNG。構造体と言っているのと同じでクラスにつける名前としては不適切 - メソッド名には、postPayment、deletePage、saveなどの動詞、または動詞句を付けるべき。
⇒メソッドはオブジェクトに命令する行為なので動詞であるべき。そうでないと何が起こるかが不明確になる。
このメソッドを呼ぶと何が起こるのかが明確な名前であるべき。 - 名前が長くなることを恐れる必要はない。内容をよく表す長い名前は、不可解な短い名前より優れている
関数について
関数を書くうえで、とにもかくにもこれは頭に入れておく必要がある。
関数の第一規則は、小さくせよ。第二の規則は、さらに小さくせよ、です。
<中略>
関数の長さが20行に達することなど、ほとんどないようにすべきです。
<中略>
関数では1つのことを行うようにせよ。その一つのことをきちんとおこない、それ以外のことを行ってはならない
これらを破ると、関数はより複雑になり、保守性が大きく下がり、手がつけられなくなる。
関数の引数については、
理想的には引数の数は0、その次が1~2、3は出来れば避けるべきで、4以上というのはよほどの理由がなければ避けるべき
とある。引数が多いと、読み手は、これを目にするたびに解読が必要になる。
また引数が多いということは前述の単一責務の原則が守れていない可能性もある。
加えて、テスト観点からも引数が多いと大変になる。
よくある「フラグ引数」はその関数が複数のことをしているというシグナルであり、明確な悪手である。
コメントについて
第一に、ダメなコードをコメントで取り繕ってはならない。
まずは適切な命名を関数や変数に行うようにし、コードの意図を伝えるようにする。そのうえでそれらを補足するような適切なコメントを書くべきである。
では適切なコメントとは何か?
本書では、それを
コードでうまく表現することに失敗したとき、それを補うのに使うものだとある。
あくまでもコードで表現しきれないことをコメントとして書き残すというスタンスが望ましい。
コードは変更され進化していくなかで、コメントは置き去りになる。いずれコードの動きとは異なった、嘘を伝えるものとなる。
嘘を伝えるコメントなど、存在しないほうが100万倍マシだ。
コメントは常に、それを最小限とするように努力すべきである。
コメントアンチパターン
- コードと同じことを自然言語で書く
⇒コード自体を上回る情報を全く提供しないコメント。これらはノイズにしかならない。 - 誤解を招きかねないコメント
⇒正確ではないコメント、あるいは将来的に正確でなくなる可能性があるコメント、現時点で古くなったコメント、不適切なコメント、正しくないコメントはただちに最新にするか取り除くべき。 - すべての関数にJavadocの記述を強制する
⇒情報量ゼロの関数ヘッダコメントや変数コメントが量産され、コードが見ずらくなる。
javadoc自体が悪ではない。"必ず書かなければならない"と厳密にルール化することが悪。 -
コメントアウトされたコード
⇒これが残っている現場は多いのではないだろうか。
本書ではこれを「コードのコメントアウトほどいやな慣習はあまりありません。絶対にやめましょう」と言い切っている。
今が60年代なら、コードのコメントアウトにも意味はあった。しかし今はgitやsvnなどのバージョン管理システムがある。消してそれが問題になることはない。今すぐに削除すべきである。
ファイルの冒頭に書かれる変更履歴情報なども同じ理由で不要になる。ノイズにしかならないので書かれない方が良い。
単体テストについて
単体テストについてはこちらの記事も書いた。
まずは単体テストがプロダクトコードに整備されているという前提にはなるが、単体テストがあればOKという訳ではない。
持たないより悪い、ということには流石にならないが、テストコードが汚く、メンテナンスに多くの時間が割かれるようでは、結局保守性が下がり、メンテナンスコストが増加してしまう。
しかし、テストコードがないと、コードの変更が問題ないかを確認することが簡単には出来ない。
これが進むと前述した「ボーイスカウトルール」的思想から「動いているプログラムを触るな」的思想にシフトしていってしまう。
テストが汚いとコード変更の妨げになる。
汚いテストコードを許容せず、テストコードをも綺麗に保つということがまずは重要になる。
単体テストは「CleanCode」の根幹になる
単体テストは、コードの
- 柔軟性
- 保守容易性
- 再現性
を維持、提供する。
まさにClean Codeの根幹 である。
理由は、単体テストがあれば変更を恐れずに行うことができるからである。そしてテスト網羅性が高いほどに、テストの信頼度が高いほどに、これらの改善が臆せず行えるようになる。
プロジェクトをCleanCodeに導く「よい単体テストコード」とは?
ではプロジェクトをCleanCodeに導く「よい単体テストコード」とはどんなものであるか、本書ではこのように述べられている。
何が洗練されたテストを作り上げるのでしょう? 3つの要素があります。読みやすさ、読みやすさ、そして読みやすさです。テストコードにおける読みやすさは、製品コードにおけるそれよりもさらに重要といえるかもしれません。
何がテストを読みやすくするのでしょう? どんなコードでも同じです。明瞭さ、単純さ、そして表現の密度です。テストにおいては、多くのことを可能な限り少ない表現で言い表す必要があります。
もちろんテストコードを読みやすくする具体的なテクニックも存在する。
構築-操作-検査パターン(いわゆるAAAパターン)。またプロダクトコードではご法度とされるような書き方も、テストコードでは許容するという二重規範の考え方も重要である。
これらを駆使し、とにかく読みやすいテストコードを書いていくことが、汚いテストコードからの脱却に繋がる。
クラス
クラスを書くうえで、とにもかくにもこれは頭に入れておく必要がある。
クラスの規則の筆頭は、小さくするということです。第二の規則は、さらに小さくするということです。
<中略>
関数の場合と同様に、まず最初の課題は「どうやって小さくするか?」です。
関数のときは、物理的な行数を測定しました。クラスの場合にはこれとは違う方法で測定します。それは責務の数です。
単一責務の原則(SRP)
クラスは、ただ一つの責務を持つべきである。
これは、「変更の原因となるものはただ一つだけ持つべき」と言い換えられる。
変更の理由が複数あるクラスは、すなわち単一責務の原則に違反していることになる。
なぜこれが重要なのか?
変更理由が複数あるクラスは、変更の衝突が起こる。
変更理由AとBが同じクラスに混ざることで、Aを直すだけのつもりがBの機能を壊すということに繋がりかねない。
さらに前述した単体テストが難しくなったり、このクラスを呼び出すクライアント側が影響範囲を気を付けなければならなくなるなど問題も多い。
単一責務の原則は、オブジェクト指向設計の概念として最も重要なもののひとつである。
小さなクラスが溢れかえるのは、全体が見えにくくなるという懸念について、本書ではこのように書かれている。
小さなクラスの集まりとして構成されたシステムというのは、大きな少数のクラスで構成されたシステムよりも可動部分が少ないのです。大きな少数のクラスで構成されたシステムと、覚えなければならないものの数は変わらないのです。
<中略>
大きなシステムというのは、大量のロジックと複雑さとを持っています。こうした複雑さに対処するのに必要なのは、開発者がいつでも直接理解しなければならない箇所を容易に探し出せるように、全体を構成するということです。
<中略>
もう一度、前者のポイントを強調しておきましょう。システムは、少数の大きなクラスではなく、多数の小さなクラスで構成する必要があります。
この単一責務の原則についてはこちらの記事でもう少し深堀している。
是非こちらも読んでみて欲しい
重複
本書の中でも特に重要なルールとされているのがこの重複
重複コードが存在するところでは、抽象化の機会が失われている。重複部分をサブルーチンや他のクラスにすることで、自身の設計の中で語彙を増やすことが出来る。別の開発者がこの抽象化機能を使用することが出来る。抽象化レベルをあげることで、コーディングを素早く行うことが出来るようになり、不具合も減る。
明らかにコピペを繰り返したようなケースは論外として、
同じ条件を扱うif/elseの連鎖。これは多能におきかえることが出来る。
さらに微妙な例として、ソースコードの行は異なるものの、似たようなアルゴリズムを持ったモジュールが挙げられる。これらはTemplateメソッドやStrategyパターンによって対処すべきである。
まとめ CleanCodeを心がけるうえで重要なのは?
結局のところ、CleanCodeを心がけるうえで重要なことは、以下の4点と言えるのではないかと、本書を読んで強く感じた。
- 意味が伝わる適切な命名
- 小さく、単一責務となっている関数
- 小さく、単一責務となっているクラス
- 適切かつ最低限のコメント
いずれも、やること自体はそれほど難しいことではないと思われる。しかし、これらが徹底されているコードとそうでないコードとでは、その質はまるで異なってくる。
これらが満たせたうえで、単体テストを整備し、リファクタリングを行える土壌を構築していくことが、CleanCodeに繋がる
最後に、CleanCodeを構築していく為の筆者のコメントを本書から引用して、この記事を締めくくりたい。
ご安心ください。筆者はいきなりこのようなプログラムを書き上げたわけではありません。さらに重要なことは、筆者は、皆さんにきれいで優雅なプログラムを一本道で書けるようになって欲しいわけではないのです。もしも筆者らがここ数十年の間に何かを学んだのだとしたら、プログラミングとは、科学というよりも工芸であるという点です。クリーンコードを書くためには、まず汚ないコードで始め、それをきれいにしていくべきです。
これは驚くに値しないはずです。小学校でこの真実を学んでいるからです。先生は生徒に作文の下書きを書くように指導します(大抵は徒労に終わりますが)。先生が我々に教えてくれたやり方というのは、まず大雑把に下書きを書き、下書きの第二版を書き、さらに何度か下書きを重ね、最後に最終版を得るというものでした。先生が我々に教えてくれようとした、きれいな作文を書くという方法は継続的改良だったのです。