ソフトウェアは書かれる回数より読まれる回数の方が多い


本書概要

名著として有名な
レガシーコードからの脱却
――ソフトウェアの寿命を延ばし価値を高める9つのプラクティス

を読んだので,内容をまとめる.
本書は2部構成になっており,前半はレガシーコードの説明とコストについて,後半は”良い”ソフトウェアを書くための9つのプラクティスについての説明がなされる.
レガシーコードとは

  • バグを多く含む
  • 壊れやすい
  • テストがない,あるいはテスト不可能
  • 可読性が低い

といった特徴のコードである.
そしてレガシーコードで作られたソフトウェアの保守・拡張には多大な費用と時間と労力が必要となる.
よって,レガシーコードを書くのをやめよう,というのが前半である.
後半はレガシーコードではない,”良い”コードを書くためのプラクティス,つまりセオリー的なものの紹介だが,具体的な方法論はおまけ程度である.
メインは「なぜ」良くないのか,「なぜ」そうするのかという「なぜ」の部分だ.
「なぜ」ウォーターフォールからレガシーコードが生まれやすく,「なぜ」アジャイルではそうではないのか.
これをしろ,ではなくその背景にある原則についての本だから,多読に耐えうる名著といわれるのだろう.
話が逸れたが,後半のなかで特に強調されていたメッセージを抜粋すると,
テストを先に書こう
である.
いままでテストをコードを書き終えた後に作成していた人,あるいはほとんどテストを書いたことのない人も本書を読めば,どうしていままで先にテストを書いていなかったのだろうかと思うようになるはずである.

小さなバッチで作る

そもそもなぜウォーターフォール開発によって生まれたレガシーコードはバグまみれで,リリース直前にデバッグ作業に追われることになるのか.
それは個々のコードを最後の最後に統合し,テストするからである.
誰もその瞬間までソフトウェアが動くかどうか分からない.
そうならないよう小さなバッチでソフトウェアを作るべきなのである.
すなわち短いスパンで統合を行い,動くソフトウェアを開発し続けるのである.
そのためには統合する前に書いたコードが動くこと確認する必要がある.
先にテストを書こう

CLEANコードを書こう

CLEANコードとは
Cohesive:凝集性があり,各クラスが単機能
Loosely coupled:疎結合
Encapsulated:カプセル化されており,やろうとしていることからやり方が切り離されている
Assertive:メソッドを配置する場所は依存データがある場所であるという断定性がある
Nonredundant:意図しない冗長さがない
コードのことである.
著者はCLEANコードが"良い"コードであり,保守性の高いソフトウェアへの近道であるとしている.
そしてCLEANコードとはすなわちテスト可能なコードだ.
ふるまいの単位で分割され,単一の理由で失敗するユニットテストでテスト可能なコード.
逆に言うと先にテストを書き,のちにテストに合格するコードを書けば,それはテスト可能なコード,すなわちCLEANコードということになる.
先にテストを書こう

テストでふるまいを明示する

本記事のタイトルにもしたのだがソフトウェアは書かれる回数より読まれる回数の方が多いそうだ.
コードは書いて終わりではない.
ソフトウェアは必ず変更が必要になる.
自分が書いたコードを変更するのは,数か月後の自分かもしれないし,チームの他の人かもしれない.
いずれにしろコードとドキュメントを行き来しながら,コードを壊さないように恐る恐る改変を加えるのは大変な労力だろう.
しかし,ユニットテストがあればコードのふるまいが把握しやすく,かつどこが動いていてどこが動いていないのかも一目でわかる.
これもテストを先に書き,それに合格するだけの最小のコードを書くからこそ,カバレッジ100%が達成できテストが生きた仕様書になるのである.
先にテストを書こう

以下,読書メモ

既存の多くのコードは拡張性に乏しく、保守にも多大なコストを要する
要件を最初に全て書き出し、それらをコードに落とし込み、最後に統合するという開発手法では、そうならざるを得ない
要件(具象)からスタートすると、部分最適化された変更困難なものになる
そこでそのソフトウェアあるいは機能を、誰が何のために使うのかというストーリー(抽象)を起点として対話的に開発を進める
細かいやり方は開発者に委ねることで開発者の裁量が広がり柔軟で保守性に優れた製品になる
その際、開発者は全員共通の原則とプラクティスを持っていなければならない
原則とは目的、プラクティスとは手段である
プラクティス1 やり方より、先に目的・理由・だれのためかを伝える
ストーリーから明確な受け入れ基準を設けて開発に取り組むことで作り過ぎ、作り込み過ぎを防ぐことができる
プラクティス2 小さなバッチで作る
機能、タスクを適切な大きさまで細分化し、作る・統合・テストのサイクルを小さくする
これには機能をカプセル化できる、バグを混入した瞬間に見つけ潰すことができるというメリットがある
プラクティス3 継続的にデプロイ可能にする
継続的インテグレーションはプロジェクトの心臓の鼓動である
ビルドサーバーを用意し、新しいコードが追加されたら即時自動でシステム全体をリビルド・テストししフィードバックを返す
コードだけでなく関連する全てをバージョン管理しておくことも、フィードバックをバグ修正に活かすために必要である
プラクティス4 協力しあう
ペアプログラミングかそれに準じる協働作業によって開発することで、問題意識・目標水準・コーディングルールがチームメンバーで共有されシステムの保守性は格段に向上する
プラクティス5 CLEANコードを作る
高品質なコードは、各クラスが単機能であり(凝集性:Cohesive)、疎結合であり(Loosely coupled)、やろうとしていることからやり方が切り離されており(カプセル化:Encapsulated)、メソッドを配置する場所は依存データがある場所であり(断定的:Assertive)、意図しない冗長さがない(非冗長:Nonredundant)
プラクティス6 まずテストを書く
まずテストを書いてから,実装を書く
ゆえにテストはまず失敗してから,合格するがそれでいい
テストが失敗するのを確認するのはテストのテストであり,先にテストを書くことで,プログラマはテスト可能な,テストをパスするのに十分なコードだけを書くことができる
プラクティス7 テストでふるまいを明示する
ユニットテストはふるまいの単位で書く
そしてテストは単一の既知の理由で失敗するようにする
それによりテストは生きた仕様書になり,リファクタリング時のセーフティネットになる
プラクティス8 設計は最後に行う
”ソフトウェアは書かれる回数より読まれる回数の方が多い”
プラクティス9 レガシーコードをリファクタリングする
リファクタリングの際もテストファースト
機能追加の際もテストファースト
既存コードに対するテストを作り,テストをパスするように機能追加の準備をする
追加コードのテストを作り,実装を書く