経緯
- およそ7年前にも読んだことがある
- その時の感想。「普通の当たり前のことが書いてあるな」
- 仕事でコードレビューをする機会が多くなる
- コードレビュー大変さを知る。同時に読みやすいコードの大切さを実感する
- 読みやすさとは?
- もう一度、リーダブルコードを読もう
- 何度も見返せるようにポイントをまとめる
第1章 読みやすさの基本原理
- 他の人が最短時間で理解できること
- 理解するのに時間が短縮されれば生産性も上がる
第Ⅰ部 表面上の改善
第2章 名前に情報を詰め込む
- 変数名は短いコメントだと思え
- 曖昧な単語は避け、明確で正確な名前をつける
- 「カラフル」な単語を探す。状況に合わせて適切な単語を選ぶ。
send: deliver, dispatch(発送する), announce, distribute, route(発送する)
find: search, extract, locate, recover(取り出す)
make: create, set up, build, generate, compose(構成する), add, new
-
tmp
やretval
などの汎用的な名前を避ける - でも本当に
tmp
(生存時間が数行の一時的な情報の保管)なら tmp でも全然良し - スコープが短ければ短い変数名(1文字とか)でも良い。逆にスコープが大きい場合は、短い名前はNG
- ループイテレータのインデックス
i
,j
,k
も複数のイテレータがある場合は説明的な名前にするよ良い (club_i
,members_i
,users_i
)
if (clubs[club_i].members[members_i] == users[users_i]) {
- 汎用的な名前を使われているのは、ほとんど怠慢。少しだけでも時間を使って名前を考える習慣を
- 変数名に大切な情報を追加する。ミリ秒を表す変数名には後ろに _ms をつける
- 不要な単語は省略。
convertToString()
はtoString()
で十分伝わる - 大文字やアンダースコアに意味を含める。クラスのメンバ変数にアンダースコアをつける
offset_
3章 誤解されない名前
-
filter
,length
,limit
など曖昧な単語を使用しない -
filter
=>select
(選択する) orexclude
(除外する) -
clip
=>remove
(最後) ortruncate
(周辺を除去) -
length
=>max_length
- 限界値を含める時は
max
,min
- 包含的範囲は
first
,last
- 包含/排他的範囲は
begin
,end
- bool値は頭に
is
,has
,can
,should
4章 美しさ
コードを読みやすくするための余白・配置・順序についての3つの原則
1. 読み手が慣れているパタンと一貫性のあるレイアウトを使う
- これはLintに任せる
2. 似ているコードは似せてるように見せる
-
複数のコードブロックで同じようなことをしていたら、シルエットも同じようにする
例)同じ処理をしている箇所をメソッドにまとめる -
「見た目をよく」することで表面上の改善だけでなく、コードの構造の改善にもなる
3. 関連するコードをまとめてブロックする
- 宣言をブロックにまとめる
- コードを段落に分割する。空行を使って大きなブロックを論理的な「段落」に分ける
5章 コメントすべきことを知る
コメントすべきでない
- コードからすぐに抽出できるコメント
- ひどい変数や関数の名前はコメントをつけずに名前を変える
記録すべき自分の考え
- なぜコードが他のやり方ではなくこうなっているのか
- コードの欠陥に記法を使って示す
FIXME:
TODO:
- 定数にコメントする。マジックナンバーにまつわる背景。
読み手の立場になって考える
- 質問されそうなことを想像する
- ハマりそうな罠を告知する
- 平均的な読み手が驚くような動作は文書化しておく
- ファイルやクラスには「全体像」のコメントを書く
- 読み手が細部に捕われないように、コードブロックにコメントをつけて概要をまとめる
6章 コメントは正確に簡潔に
- 複数の物を指す可能性がある「それ」や「これ」などの代名詞を避ける
- 関数の動作はできるだけ正確に説明する
- 入出力のコーナーケースに実例を使う
- コードの意図は、詳細レベルではなく、高レベルで記述する
- よくわからない引数にはインラインコメントを使う
Funciton(/* arg = *? ...)
- 多くの意味が詰め込まれた用語や表現を追加って、コメントを簡潔に保つ
第Ⅱ部 ループとロジックの単純化
7章 制御フローを読みやすくする
- 条件式の並び順は、左側「調査対象」の式。変化する。右側「比較対象」の式。あまり変化しない。
- 英語の文章として自然な感じで
Good
if (length >= 10)
while (bytes_received < bytes_expected)
Bad
if (10 <= length)
while (bytes_expected > bytes_received)
8章 巨大な式を分割する
- 説明変数:式の意味を説明してくれる変数
Before
if line.split(':')[0].strip() == "root":
After
username = line.split(':')[0].strip()
if username == "root":
- 要約変数:大きなコードの塊を小さな名前に置き換えて、管理や把握を簡単にする変数
Before
if (request.user.id == document.owner_id) {
// ユーザはこの文書を編集できる
}
if (request.user.id != document.owner_id) {
// 文書は読み取り専用
}
After
const user_owns_document = (request.user.id == document.owner_id);
if (user_owns_document) {
// ユーザはこの文書を編集できる
}
if (!user_owns_document) {
// 文書は読み取り専用
}
- 短絡評価の悪用
Before
assert((!(bucket = FindBucket(key))) || !bucket->IsOccupied());
たった1行のコードだけど、しばらく立ち止まって考えなきゃいけない感じ。
After
bucket = FindBucket(key);
if (bucket != NULL) assert(!bucket->IsOccupied());
全く同じものだけど、ずっと理解しやすくなった。
9章 変数と読みやすさ
- 中間結果の変数を削除する
- 制御フロー変数を削除する
- 変数のスコープを縮める。 クロージャ(JS)を使って、プライベート変数を作る
- 変数定義の位置を下げる。 関数の先頭に変数定義をまとめるのではなく、変数を使う直前に移動する
第Ⅲ部 コードの再構成
10章 無関係の下位問題を抽出する
- 無関係の下位問題を積極的に抽出して見つけて抽出すること
- 「このコードの高レベルの目標は何か?」と自問
- 「高レベルの目標に直接的に効果があるのか?あるいは、無関係の下位問題を解決しているのか?」と自問
- 無関係の下位問題を解決しているコードが相当量あれば、それらを抽出して別の関数にする。
- 抽出された関数は完全に自己完結しているので、自分がどのように使われているか知らない。再利用性が高い
- 純粋なユーティリティコード
- 抽出されたコードが独立していれば、そのコードの改善が楽になる
- インタフェースが複雑なライブラリはラッパーしてシンプルにする
- やりすぎ注意。小さな関数を作りすぎて、実行パスがあっちこっちに行って逆に読みにくくなる
11章 一度に1つのことを
- コードをデフラグする
- 一度に複数のことをするコードは理解しにくい
- コードは1つずつタスクを行うようにしなければいけない
- 「関数は一度に1つのことを行うべき」という考え方と同じ
- 読みにくいコードがあれば、そこで行われているタスクを全て列挙する
- そのタスクごとにコードを分割する
12章 コードに思いを込める
- おばあちゃんにがわかるように説明できなければ、本当に理解したとは言えない --アルバート・アインシュタイン
- コードを簡単な言葉で説明する
- コードの動作を簡単な言葉で同僚にもわかるように説明する
- その説明の中で使っているキーワードやフレーズに注目する
- その説明にあわせてコードを書く
- 問題や設計をうまく言葉で説明できないのであれば、何かを見落としているか、詳細が明確になっていないということ
- プログラム(あるいは自分の考え)を言葉にすることで明確な形になる
- 説明で使っている単語やフレーズをよく見れば、分割する下位問題がどこにあるかわかる
13章 短いコードを書く
- 最も読みやすいコードは、何も書かれていないコードだ
- 不必要な機能をプロダクトから削除する。過剰な機能は持たせない
- 最も簡単に問題を解決できるような要求を考える
- 定期的にすべての API を読んで、標準ライブラリに慣れ親しんでおく
- ライブラリの再利用
- 車輪の再発明をしない
14章 テストの読みやすさ
- 新しいテストの追加や
- テストのトップレベルはできるだけ簡潔にする。入出力のテストはコード1行で記述できるとよい
- テストが失敗したらバグの発見や修正がしやすいようなエラーメッセージを表示する(適切なアサーション関数を使用する)
- テストに有効な最も単純な入力値を使う
- テスト関数に説明的な名前をつけて、何をテストしているのかを明らかにする
15章 「分/時間カウンタ」を設計・実装する
- 時間バケツの実装は読むだけでは理解できなかった
- 自分でコードを書いてみると良いと思う