$shibayu36->blog;

クラスター株式会社のソフトウェアエンジニアです。エンジニアリングや読書などについて書いています。

職業プログラマーなら必ず読むべき「Code Complete」

Code Completeの上下巻を読んだ。


読んだ感想としては、職業プログラマーなら必ず読むべき本だなと感じた。

この本ではソフトウェアコンストラクションに関する話題を扱っている。この本の中でソフトウェアコンストラクションとは、詳細設計、コーディングやデバッグ単体テストなどなど、要求定義が終わった後、ソフトウェア製作に必要なプロセス全般のことを指している。

主なテーマとして、どうやってソフトウェアにおける複雑さを減らすことが出来るのか、について書かれている。そのテーマをいろいろな観点から説明されている。例えば以下の様な観点がある。

  • 上流工程の欠陥によるコンストラクションへの影響
  • コンストラクションにおける設計
  • クラス、ルーチン、制御構造などの利用指針
  • リファクタリング
  • ペアプロなどのコラボレーティブプログラミング


この本の中ではそれぞれの話がソフトウェア開発の研究にもとづいて説明されており、仕事でプログラムをこれまでしていた人にとっては、これまでプログラムを書いている時にもやもやしていた思いが晴れる気持ちがすると思う。例えば、コードレビューをするときに、これって感覚的にはダメだけど、実際にどうかわからないから指摘しづらいなーという部分がこの本を読むことによって、研究としてもダメだったという客観的な知識が身につく、ということがあった。

この本は以下の様な人におすすめできると思う。

  • 仕事などでプログラミングを書いていて、良い設計やコードについて考えようと思っている人
  • エンジニアリングのマネージャなどをやっている人

経験がないと理解できない内容も多いので、経験不足だけどとりあえず設計やコードの書き方について学びたいという人はリーダブルコードインターフェイス指向設計とかを読むといいと思う。

以下、心に残ったことについて書いていく。

心に残った部分

ソフトウェアは建築に近い

  • 理由は無いけど、確かに、って思ってしまった
  • 出来てしまったら修正困難だけど、修正しないと潰れるみたいな気持ち

複雑さを減らす

  • ソフトウェアを開発するのは人である
  • そのため複雑すぎるものは理解が出来ない
  • 一度に頭のなかで考える項目を減らすこと
    • 所詮人間は5~9個のことしか覚えられない
  • 見せる必要のない情報はクラスやルーチンの中に適切に隠蔽する

要求段階での欠陥修正コスト

  • 要求段階での欠陥を、コンストラクション時に修正するには要求時の5~10倍、リリース後に修正するには10~100倍かかる
    • ちゃんと仕様を確定せずにコードを書き始めてしまって、仕様が変わって大変なことになっている例をよく見る
    • リリース後むっちゃ大変な思いをして直している自分がいる

書き手の便宜より読み手の便宜を優先する

  • 読む回数のほうが書く回数よりも多い

3レベル以上のif文のネストを理解できる人はほとんどいない

  • あー、、、ってなった
  • きちんとクラス化、ルーチン化して一度に見るネスト数を減らす

プログラムに先の事を考えたコードが含まれている

  • 最初から過剰に抽象化するのは失敗のもと
  • 抽象化時に想像できていないことが必ず起き、その抽象化によってコードの改良を困難にする

コメントを入れるか入れないか

  • これまでのコメント論をきちんと表しており、この部分は本当におもしろかった
  • 結論としては
    • ややこしいコードの意味をコメントで説明するくらいならコードを綺麗にすべきだが、コードよりも抽象的レベルで説明するコメントは絶対に必要
    • 特に複数人なら。一人でも未来の自分に向けて必要。
    • しかし、コードを復唱するコメントは最悪である
  • コメントの最適な数としては大体10ステートメントに1個くらいで、プログラムが最も明瞭になるという研究がある

本を読んでからやり始めたこと

Code Completeを読み始めてから、設計の重要性を改めて思い知り、最近はコードを書く前に設計の絵を書いてみる、擬似コードを書いてみる、などのことをやり始めた。

設計の絵を書いてみる

すごく雑だけど例えば以下の様な図を手書きでさらっと書いて、処理の流れを確認してる。

f:id:shiba_yu36:20130304110534p:plain:h300

擬似コードを書いてみる

図を書いた後以下のようにクラスを作る前に擬似コードをコメントで書いてみて、頭の中で設計する。その後コードを書きながら頭の中の設計を少しずつ変えて最適化していく。

package Sample::Batch::GraphUserActivity;

use utf8;
use strict;
use warnings;

sub run {
    my ($class, %args) = @_;

    # 引数target_date, limit_daysを受け取る

    # 一日分の登録ユーザを取ってくる

    # 登録ユーザの投稿を取得する

    # それぞれの投稿が登録からlimit_days以内か確認する

    # 結果をHRForecastにpost
}

1;

こういうことをするだけで事前にいろいろな設計ミスを認識することが出来た。

まとめ

上に心に残ったことを書いたけど、Code Completeは内容が非常に多く、人によって心に残ることが違うと思うし、何度も読み返すと、その時の経験に基づいた新しい発見が得られると思った。仕事でプログラミングをしている人は是非読んだほうが良いと思います。

また、まだ経験が浅い人は以下の様な本もおすすめです。

読んだ時に書いたメモとか

読んでた時に書いたメモをローカルから削除するためにコピペしておく。

上流工程での欠陥がどれほど影響するか

  • 要求段階での欠陥を、コンストラクション時に修正するには要求時の5~10倍、リリース後に修正するには10~100倍かかる
  • 感覚としても正しい
  • ちゃんと仕様を確定せずにコードを書き始めてしまって、仕様が変わって大変なことになっている例をよく見る

コンストラクションにおける設計

  • 情報隠蔽による複雑さへの対処
  • 何を隠蔽する必要があるか
  • 反復による設計
    • これはよくある
    • テストを書きながら、あーこれは良くなかったとか言って書きなおしたり
    • 一回全部捨てて一から書きなおしたり

クラスの作成

  • 書き手の便宜より読み手の便宜を優先する
    • 読むほうが多い
  • あるクラスが他のクラスのことについて知らないようにしたほうがうまくいくことが多い気がする
    • 他のクラスのインスタンスのメソッドを呼ばないとか
    • データは全部引数で渡すとか

ルーチン

  • ルーチンを作成する最も重要な理由は、プログラムを頭で理解しやすくすること
  • ルーチンごとの凝集度を強くする
    • 各ルーチンは一つのことしかしない
    • 機能的凝集
  • 変数名は9〜15文字が最適という研究がある
  • ルーチンの長さは50〜150行が最適という研究がある
    • とはいえ長さに囚われすぎてはいけない
    • 短くしようとわかりにくいプログラムで複雑さをあげては意味が無い

ループ

  • 単純なものからループを作り上げる
    • 中身を作ってループで囲んで...

制御構造の問題

  • 3レベル以上のif文のネストを理解できる人はほとんどいない

コードの改良

  • エラーはエラーを発生しやすい一部のクラスやルーチンに集中する傾向にある

リファクタリング

  • プログラムに先の事を考えたコードが含まれている
    • 過剰な最適化
    • 先のことを予想しきれていない
  • リファクタリングは一度に1つずつ行う
    • 一つ一つテストすることにより、動かなくなった時の修正コストを減らす
  • 一度も変更したことがないコードをリファクタリングする必要はない。ただし、コードの一部に触ったら、必ず見つけた時よりも良い物にしてから離れるようにする

読めば分かるコード

  • コメントを入れるか入れないか
    • むっちゃ面白い
    • 最終的には
    • コードよりも抽象的レベルで説明するコメントは絶対に必要
      • 特に複数人なら。一人でも未来の自分に向けて
    • ややこしいコードの意味をコメントで説明するくらいなら、コードを綺麗にすべき
    • コードを復唱するコメントは最悪
  • コメントの最適な数
    • 大体10ステートメントに1個くらいで、プログラムが最も明瞭になるという研究