見出し画像

「リーダブルコード」読了。

はじめに

積読しがちな技術書を読み終えるために、日々1章ずつ読み進めることをしていました。

今回「リーダブルコード」を読み、日々のアウトプットをnoteにまとめました。

よろしければご活用ください。

第1章:理解しやすいコード

コードを書く上での大原則
・コードは理解しやすくなければいけない。

読みやすさの基本定理

・コードは他の人が最短時間で理解できるように書かなければいけない。
・他の人とは、未来の自分も含む。
・コードを書く時は、他の人と、未来の自分がわかりやすい/理解しやすいようなコードにすることが大事。

第2章:名前に情報を詰め込む

考え方
・名前に情報を詰め込むこと。

実践方法

・一目でわかる単語を選択する。
・気取った言い方や抽象的な名前を避け、具体的に、的確に言い表す。
・tmpやretvalのような汎用的な名前は避けること。
・tmpという名前は、生存期間が短く、一時的な保管が最も優先される時の使用はOK。

第3章:誤解されない名前

考え方
・名前が「他の意味と間違えられることはないだろうか?」と何度も自問自答する。

方法や具体例

・関数名が「filter()」であると、何をどうフィルターしているのかがわからない。
・名前をつけるときに、あらゆる可能性を考える。
・限界値を明確にするには、名前の前に「max_」や「min_」をつける。
・範囲を指定するときは「first」と「last」を使用する。
・包含/排他的範囲には「begin」と「end」を使用する。ここでいう排他的範囲とは、(おそらく)配列を例に挙げたとき、最後の値をちょうど超えたところまでの範囲のことをいっている。
・ブール値の変数やブール値を返す関数の名前を選ぶときには「tre」と「false」の意味を明確にする。is,has,can,shouldを使うとわかりやすくなる。
・最善の名前とは、読む人に誤解を与えない名前のこと。作成者の意図をたやすく、正しく理解できること。

第4章:美しさを意識する

3つの原則
・読み手が慣れているパターンと一貫性のあるレイアウトを使う。
・似ているコードは似ているように見せる。
・関連するコードをまとめてブロックにする。

具体的方法

・縦を揃える。
・関数の引数や不等号の位置を合わせるなど。一見書きづらくなるのではと思うけど、間違いに気付きやすいという点はメリットかもしれない。
・一貫性と意味のある並びにする。
・変数を複数定義する時などに、意味を持たせた並びにすると過毒性が上がることがある。例えば、お問い合わせフォームで使用する変数を、上から順に並べるなど。
・宣言をブロックにまとめる。
・人間の脳はグループや階層をひとつの単位として考えるようにできている。同じ意味を持ったもの同士をまとめる。例えばrailsのmodelで、バリデーション同士、enum同士でまとめるなど。
・コードを「段落」に分割する。
・文章が複数の段落に分かれているように、コードも段落で分割しまとめる。長い処理のコードに対して、処理の単位でまとめる、意味のある単位でまとめる、コメントをつけるなどして見やすくする。

第5章:コメントすべきことを知る

考え方
・コメントの目的は、書き手の意図を読み手に知らせること。
・コードからすぐに分かることをコメントに書かないこと。

実践、具体例など

・関数や変数の名前がひどく分かりづらいのなら、コメントをつける以前に名前を変更する方がいい。
・ひどいコードと優れたコメントよりも、優れたコードの方が良い。
・コードの欠陥に対してコメントを残しておく。

# プログラマがよく使う記法

TODO:  あとで手を付ける
FIXME: 既知の不具合があるコード
HACK:  あまり綺麗じゃない解決策
XXX:   危険。おおきな問題がある

・何も知らない人が読んだ時、ハマりそうな罠がないか考える。
・「このコードを見てびっくりすることはなんだろう?どんなふうに間違えて使う可能性があるだろう?」と自分に問いかける。

・新しくチームに入った人にとっては、全体像の理解が一番難しい。そのため口頭で教えるようなコメントがあると、新しいチームメンバーにとってはありがたい。
・短い文章でもいいから、理解に時間を要すると思われるものに関してはコメントを残しておくといいかも。

t-wadaさんの言葉

コードには      How
テストコードには  What
コミットログには  Why
コードコメントには Why not

を書こう

※引用元ツイート
https://twitter.com/t_wada/status/904916106153828352

第6章:コメントは正確で簡潔に

考え方
・コメントは領域に対する情報の比率が高くなければいけない。

実現方法や具体例
・あいまいな代名詞は避ける。
・コメントに「これ」「それ」などの言葉があると何を指しているのかわかりづらくなる。
・正確なコメントとなるように実例を入れて書くのも良い。
・コードの意図を書く。
・コードの意味や説明(What)をそのまま書いても価値がない。
・コードは書いている時に考えたことを読み手に伝えるためのもの。
・t-wadaさんの言う「Why not」を書くということにもつながる。
・情報密度の高い言葉をつかう。
・多くの意味が詰め込まれた言葉や表現を使って、コメントを簡潔にたもつ。

第7章:制御フローを読みやすくする

考え方
・条件やループなどの制御フローはできるだけ「自然」にする。
・コードの読み手が立ち止まったり読み返したりしないように書く。
・行数を短くするよりも、他の人が理解するのにかかる時間が短くなっているかを考える。
・コードを変更するときは、コードを新鮮な目で見る。一歩下がって全体を見る。

実践方法
・条件式の引数の並び順は、調査対象を左側にし、比較対象を右側にすると読みやすい。
・何故読みやすいのか → 英語の用法とあっているから

# 調査対象を左、比較対象を右
if ( length >= 10 )

# 逆にすると少し読みにくくなる
if ( 10 <= length )

・if/else ブロックの並び順を変える。
・条件は否定形よりも肯定系を使う。例えばif (!debug)ではなく、if (debug)を使用する。
・単純な条件を先に書く。
・関心を引く条件や目立つ条件を先に書く。
・ただ状況によって何が関心を引くのかは変わってくるので一概に肯定系とはいえない。その場で判断する。

if (!url.HasQueryParameter("expand_all")) {
 response.Render(items);
 ...
else {
 for (int i = 0; i < items.size(); i++) {
   items[i].Expand();
 }
 ...
}

# 上の式だと、まず"expand_all"が気になってしまう。先にこの条件である時の処理を書いてしまうといい。

if (url.HasQueryParameter("expand_all")) {
  for (int i = 0; i < items.size(); i++) {
    items[i].Expand();
  }
  ...
else {
 response.Render(items);
 ...
}

・三項演算子には良し悪しがある。大事にすべきは行数を短くするよりも、他の人が理解するのにかかる時間が短くなっているかどうか。
・do/whileループを避ける。
・条件が後に来る為わかりづらい。
・ネストを浅くする。
・ネストが深いコードは分解して書く、早めに返してネストを返すようにすると良さそう。

第8章:巨大な式を分割する

考え方
・巨大な式は飲み込みやすい大きさに分割する。
・「頭がいい」コードに気を付ける。あとで他の人がコードを読むときにわかりにくくなる。

実践方法や具体例
▷説明変数
・式を表す変数名にする。変数名で式を説明する。
・簡潔な言葉で言い表すことで、コードを文書化できる。
・コードの主要な概念を読み手が認識しやすくなる。

▷要約変数
・式を説明する必要がない場合でも、式を変数に代入しておくと便利。大きなコードの塊を小さな名前に置き換えて、管理や把握を簡単にする変数のこと。

第9章:変数と読みやすさ

変数に関する3つの問題
・変数が多いと変数を追跡するのが難しくなる。
・変数のスコープが大きいとスコープを把握する時間が長くなる。
・変数が頻繁に変更されると現在の値を把握するのが難しくなる。

対処法、考え方

・役に立たない一時的な変数は削除する。
・グローバル変数は避ける。
・変数のことが見えるコード行数をできるだけ減らす。
・グローバル変数に限らず、全ての変数の「スコープを縮めること。
・JavaScriptは、変数の定義にvarをつけないと、その変数はグローバルスコープにはいってしまう→変数定義するときはvarをつける。
・変数は一度だけ書き込む。永続的に変更されない変数は扱いやすい。

第10章:無関係の下位問題を抽出する

考え方
・関数やコードブロックを見て「このコードの高レベルの目標は何か?」と自問する。
・コードの各行に対して「高レベルの目標に直接的に効果があるのか?あるいは、無関係の下位問題を解決しているか?」と自問する。
・無関係の下位問題を解決しているコードが相当量あれば、それらを抽出して別の関数にする。

例を使って読み解く
無関係な下位問題とは?

例えば、あるコードブロックの一部に難しい計算式が入っていたとする。
このコードブロックの本来の目的は、ある目的・目標があり、その一部として計算を行っている。

難しい計算をすること自体が本来の目的ではない。

計算部分は高レベルの目標に直接的に効果がない(と思われる) => 「無関係」の下位問題。

改善策として計算部分を抽出して別の関数にする => 無関係の下位問題の抽出。

つまり、無関係の下位問題の抽出とは、本来の目的に直接的に効果がない部分を別にすること、という意味合いになる。

無関係の下位問題を抽出したことにより得られること

・本来のコードが読みやすくなる。
・抽出した部分の個別テストができるようになる。
・抽出した部分が将来的に再利用可能となる。

注意点

・小さなコードにしすぎるとかえって読みづらくなるので注意。

まとめ

・無関係な下位問題を抽出する → プロジェクト固有のコードから汎用コードを分離すること。一般的な問題を解決するライブラリやヘルパー関数を作っていけば、プログラムに固有の小さな核だけが残る。

第11章:一度に一つのことを

考え方
・コードは一つずつタスクを行うようにしなければならない。

実践方法、具体例
▷手順
1. コードが行っている「タスク」を全て列挙する。タスクの定義は緩やか。
2. タスクをできるだけ異なる関数に分割する。少なくとも異なる領域に分割する。

# 例:Upを押すと+1点、Downを押すと-1点となるシステムがあるとする


# タスクがこんがらがっているコード

var vote_changed = function (old_vote, new_vote) {
 var score = get_score();
 
 if (new_vote !== old_vote) {
   if (new_vote === 'Up') {
     score += (old_vote === 'Down' ? 2 : 1);
   } else if (new_vote === 'Down') {
     score += (old_vote === 'Up' ? 2 : 1);
   } elseif (new_vote === '') {
     score += (old_vote === 'Up' ? -1 : 1);
   }
 }  
 set_score(score);
};  


# 上のコードを、タスクごとに分けて書いたものが下のコード。
# 「old_vote と new_voteを数値にパースすること」、「scoreを更新すること」のふたつに分けている

// Upの時+1,Downの時-1
var vote_value = function (vote) {
  if (vote === 'Up') {
    return +1;
  }
  if (vote === 'Down') {
    return -1;
  }
  return 0;
};

// scoreの更新
var vote_changed = function (old_vote, new_vote) {
  var score = get_score();
 
  score -= vote_value(old_vote);  // 古い値を削除
  score += vote_value(new_vote);  // 新しい値を追加
 
  set_score(score);
};

第12章:コードに思いを込める

メモ
・書いているプログラムを、簡単な言葉で説明できるかどうかを考える。
・実装中、実装後のリファクタリング時に有効。
・自分以外の誰かが読んだときに簡単に伝わるプログラム(言葉)になっているかどうかという観点を持つ。
・問題や設計をうまく言葉で説明できないのであれば、何かを見落としているか、詳細が明確になっていないということ。

第13章:短いコードを書く

考え方
・最も読みやすいコードは、何も書かれていないコード。

実践方法、具体例

・コードが増えるとテストや文書や保守が大変になる。また動作も重くなってしまう。
・新しいコードを書かないようにするには不必要な機能をプロダクトから削除する。過剰な機能はもたせない。
・最も簡単に問題を解決できるような要求を考える。
・定期的に標準ライブラリを読み込み、ライブラリで可能な動作の知識をためておく。
・プログラムを書こうとする際、過剰に機能を組み込んで作ろうとしないこと。要求を最小限にして作ること。

第14章:テストと読みやすさ

考え方
・他のプログラマが安心してテストの追加や変更ができるように、テストコードを読みやすくする。
・テストには最もきれいで単純な値を選ぶこと。コードを完全にテストする最も単純な入力値の組み合わせを選択しなければならない。

実践方法や具体例

・一般的な設計原則として「大切ではない詳細はユーザーから隠し、大切な詳細は目立つようにする」というものがある。これはテストコードも同様。

・テストのための適切な入力値を決める。

# 例:検索結果のスコアを降順でソート、かつマイナスのスコアを削除するメソッドのテストについて

# CheckScoreBeforeAfterメソッドは「第一引数の数値を降順ソート&マイナスの値を削除」するもの
CheckScoreBeforeAfter("1, 2, 3", "3, 2, 1");

# マイナスの値がなくテストが不十分なので、マイナスの値を追加
CheckScoreBeforeAfter("1, 2, -1244.22, -233.1111, 3", "3, 2, 1");

# マイナスの値が不必要に複雑なので、最小限にし単純化
CheckScoreBeforeAfter("1, 2, -1, 3", "3, 2, 1");

・一つの機能に複数のテストを書く。
・コードを検証する完璧な入力値を一つだけ作るのではなく、小さなテストに分割して作る方が読みやすくて効果的。不具合も見つけやすくなる。

# 例
CheckScoreBeforeAfter("1, 2, 3", "3, 2, 1");       // ソート
CheckScoreBeforeAfter("1, 2, -1, 3", "3, 2, 1");   // マイナスは削除
CheckScoreBeforeAfter("1, 1, -1", "1, 1");         // 重複は許可
CheckScoreBeforeAfter("", "");                     // 空の入力は許可

・テストの名前は意味のある物にする。テストする関数や状況を表したものにすること。

注意

・テストコードに集中しすぎることは避ける。
・テストのために本物のコードの読みやすさを犠牲にしてしまうこと。
・テストのカバレッジを100%にしようとしてしまうこと。コスパも悪く、現実的ではない。

第15章→割愛


さいごに

▷自然に読みやすいコードをかけるようになるために必要なこと
(1)実際にやること
(2)当たり前にすること
(3)コードで伝えること

(1)実際にやること

・実際にやってみると、学んだことを実際のコードの読みにくさに「気づかない」「気づけない」ことが多い。
・だから他の人に読んでもらうことが良い。
・仲間と一緒に、お互いのコードをよくしていくと良い。
・フィードバックをどんどんもらう。

(2)当たり前にすること
・読みやすいコードが少し書けるようになってくると、既存コードの読みにくさに気づき始める。
・だが、読みにくいコードを読みやすくする前に一度落ち着こう。
・まずはこれからあなたが触るコードを読みやすいコードにで書いていこう。
・その上で他の人が新しく書いたコードについても同じく読みやすいコードにしていこう。
・これを続けて当たり前にしていこう。

(3)コードで伝えること
・読みやすいコードをもっと当たり前にしていく。
・新しい仲間も読みやすいコードを書くことが当たり前になるようにする。
・添削(提案)コミットをして相手に自分が意図していることを伝えてみる。
・具体的な改善案と読みやすくなる理由で読みやすいコードを伝える。

この記事が気に入ったらサポートをしてみませんか?