リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)はエンジニアであればほとんどの人が持っている、コードをきれいに書くためのノウハウが詰まっている名著です。
オススメポイント
-
コードを書くたびに何度も見返して定着させることが重要です。この記事は是非ブックマークをしておきましょう!
-
この本は世の中のWebエンジニア全員が持っていると言っても過言ではないです。エンジニアを生業としてやっていくのであればこの本はかなり重要な本なので必ず持っておきましょう!
目次
リーダブルコードはエンジニアなら必須の本なので必ず持つことをおすすめ!
リーダブルコードはエンジニアであれば必ず持っておくと良いです!
持ってないと逆に恥ずかしいと思うくらいに、エンジニアのバイブルとして有名なので、今後エンジニアを続けようという方は一冊持っておきましょう。
注意ポイント
日本語のリーダブルコードは無料で読むことは出来ないです(英語なら無料のPDFがあります)
値段も高くなく、一生使えます!
MENTAでプログラミング設計を学びたい方はこちら!評判が良いプランです!
理解しやすいコードとは?
この本の伝えたことを端的に言うと
ポイント
「理解しやすいコードを書けるようにするにはどうすればよいか?」ということである。
これは今回の記事の中で一番重要なことなので覚えておいてください!
では理解しやすいコードとは?
「他の人が短時間で理解できるように書かれているコード」
他の人が短時間で理解できるように書かれているコードとは?
例えば同僚にコードを読んでもらって、そのコードを理解するまでにかかった時間が短ければ短いほど理解しやすいコードということです。
更に「理解しやすい」というワードを深ぼると、
理解しやすいポイント
- 変更を加えやすい
- バグをすぐに見つけやすい
- 他のコードと連携するときに容易
ではこれを踏まえた上で「理解しやすいコードを書くにはどういうことを意識する必要があるのか」見ていきます。
名前に情報を詰め込む
ポイント
変数名、関数名、クラス名、型名などプログラムでは独自に名前をつけることが多々あります。
それらの名前には必ず「情報を詰め込むこと」!!
情報を詰め込むこととは?
「その名前を見ただけで、何を表す変数、関数なのかすぐに理解できること」
具体的には以下のことに気をつけましょう!
具体例
- 明確な単語を選ぶ
- 汎用的な名前を避ける(使う状況を選ぶ)
- 抽象的な名前よりも具体的な名前を使う
- 接尾辞や接頭辞を使って情報を追加する
- 名前の長さを決める
- 名前のフォーマットで情報を伝える
明確な単語を選ぶ
インターネットからページを取ってくる関数の場合の例
def GetPage(url);
よりも
DownloadPage(url);
の方が明確。
「get」という単語だとこのページはどこから取ってくるのかわからない。ローカルキャッシュ?データベース?インターネット?など様々受け取れる。
なので、インターネットから取得する場合は「download」の方が明確である。
またこれは実務では実践出来ていないプロジェクトの方が多い印象です。
というのも人によって言葉のニュアンスが様々なのと、
それをちゃんとフィルタリングする仕組みをチームで整えるのは現状出来ているプロジェクトが少ないからです。
ただこの要素は重要なので覚えておきましょう!
せめて上に出した例のような使い分けは出来るようにするのが望ましいです。
関数名に悩んだ時はこちらの記事が関数名のよく使われる単語集になっていてオススメです。
汎用的な名前を避ける(使う状況を選ぶ)
「tmp」「retval」「foo」など汎用的な名前は何を表しているかわからないので避けること!
変数名は目的やその値を意味しているのが望ましい。
function(v) {
var retval = 0;
for (var i = 0; i < 10; i++) {
retval += v[i]*v[i];
}
return retval;
}
このコードの「retval」はただ返り値ですという意味しか変数名が表してなく、悪い変数名である。
この場合はvの2乗の合計「sum_squares」というような変数名が良い。
ただ汎用的な名前でも良い場合もある
if(right < left) { tmp = right; right = left; left = tmp; }
このような場合は「tmp」という汎用的な名前で問題ない。
汎用的な名前を使って良い理由
この変数名は一時的な保管に使われているため。
まとめ
汎用的な名前を使う時はそれ相応の理由があることが重要!
今ならMENTA内で利用できる500円クーポンがもらえる
CleanアーキテクチャやDDDが学べる!設計から実装に落とし込む実践力が身につけたい方はこちら!
抽象的な名前よりも具体的な名前を使う
任意のTCP/IPポートをサーバがリッスン出来るかを確認するメソッドの場合
ServerCanStart()
よりも
CanListenOnPort()
の方がより具体性があり良い。
接尾辞や接頭辞を使って情報を追加する
重要ポイント
名前は短いコメントのようなものです。
絶対に知らせたいことは変数名に含めるべきである。
例えば
string id = 'af84ef845cd8';
のフォーマットが大切ならhex_idという変数名にするのが望ましい。
その場合接尾辞や接頭辞をうまく使うと良い。
例としてはこの表のように使うと良い
状況 | |
passwordはプレインテキストなので処理をする前に暗号化すべき | password よりも plaintext_password |
エスケープ処理がされている単語の場合 | words よりも escaped_words |
MENTAでプログラミング設計を学びたい方はこちら!評判が良いプランです!
CleanアーキテクチャやDDDが学べる!設計から実装に落とし込む実践力が身につけたい方はこちら!
名前の長さを決める
名前は長すぎると、プログラムを書く際のエディター画面を占領してしまい返ってわかりづらくなってしまう。
かといって短すぎると変数に情報がなさすぎるので良い塩梅にしましょう!
例えば
ConvertToStringという関数名はToStringにしても情報の欠損がなく伝わるので、こういう最適化をするのが望ましい。
例外ポイント
またスコープが小さい場合は短い変数名でも問題ない。
なぜならスコープが小さい分、理解するのに必要な情報が直ぐ側にあるから、短くても理解に困らないです。
名前のフォーマットで情報を伝える
これは言語によっても多少異なるが、クラス名はアッパーキャメルケース、変数名はキャメルケースなど
フォーマットが言語ごとに予め決められているので、それに習ってフォーマットを変えることで名前がわかりやすくなります。
まとめ
まとめ
名前を見ただけで情報を読み取れるようにすること!
本では更に詳しく解説されているので是非見てみると良いです。この本を持ってないと恥ずかしいというレベルなので必ず手元に置いてときましょう
誤解されない名前
名前が他の意味と街がられることは無いか?何度も自問自答したり、同僚に聞いて確認しよう!
filter関数について
データベースの問い合わせ結果を処理するコード書いている場合
results = Database.all_objects.filter("year <= 2011");
これは
- 「year<=2011」のオブジェクト
- 「year<=2011」ではないオブジェクト
どちらを示しているのかわからない。
filterが「選択する」のか「除外するのか」どちらなのかわからない曖昧な言葉だからである。
わかりやすくするには?
- 「選択する場合」はselect
- 「除外する場合」はexclude
という風にすると良いです!
限界値を含める時はmin,maxを使う
例えばショッピングカートには商品が10点までしか入らないケースを考えてみよう。
その時のマジックナンバーを
CART_TOO_BIG_LIMIT = 10
この変数名だと
「未満(10を含まない)」なのか「以下(10を含むのか)」はマジックナンバーからだとわからない。
限界値の変数はmax,minを使うとわかりやすくなる。
MAX_ITEMS_IN_CART = 10
こうすることで限界値が10まで(10を含む)ということがマジックナンバーからもわかる。
実際のプロジェクトではここまで意識できているか?
ここもまた難しい点ではあるが出来ている部分もあれば出来ていない部分もあるのが現状。
逆に言えば出来なくてもある程度はプロジェクトでは問題ないとも言えます。
ただこれも細かい話ですが重要なことなので、意識してコードを書くようにするのが良いです!
これらの例以外にも本書には載っているので、エンジニアをちゃんとやるのであれば買って必ず一読しておき、
仕事中にも何度か変数名に詰まったりしたときに読み返すのが良いです。
今ならMENTA内で利用できる500円クーポンがもらえる
CleanアーキテクチャやDDDが学べる!設計から実装に落とし込む実践力が身につけたい方はこちら!
なぜプログラムコードの美しさが大事なのか?
これは言語にもよりますがある程度リンターで解決出来ます
プログラムコードが美しいとは?
- 読み手が慣れているパターンと一貫性のあるレイアウトになっている
- 似ているコードは似ているように見せる
- 関連するコードをまとめてブロックにする
関連
なのでこの章はリンターにある程度おまかせするのが良いです。
これ以上は人やプロジェクトのさじ加減になるのでここでは明言しません。
ただ他にも述べられている事があるので、その部分は本書で見てください。
リンターでも対応出来ないことが多々書いてあります。
プログラムコードにコメントすべきこと・すべきでないこと
コメントの目的は書き手の意図を読み手に知らせること
コードを書いている時は書き手にはたくさんの大切な情報があるが、他の人がそのコードを読む時はその情報は書かれていない。
コードを読む人が見るのは目の前にあるコードだけです。
コメントを書く際に意識すべきことは以下3つ
意識すべきポイント
- コメントするべきでは「ない」事を知る
- コードを書いているときの自分の考えを記録する
- 読み手の立場になって何が必要になるかを考える
プログラムでコメントするべきではないこと
コメントを書くとその分読む時間が長くなり、また画面を占領してしまうため、なるべく不要なコメントは書かないことです。
ではコメントすべきこととすべきでないことの違いは何か?
注意ポイント
「コメントのコメントをしないこと!
例えば
// 与えられたsubtreeに含まれるnameとdepthに合致したNodeを見つける Node* FindNodeInSubtree(Node* subtree, string name, int depth);
これは価値がないコメントです。
シグネチャからsubtreeの中のnodeを見つける関数というのは容易にわかります。
もし仮に上記のようなケースでコメントを入れないとわからない場合は、名前が悪いケースになるので、名前を変えることをしましょう!
プログラムコードを書いているときの自分の考えを記録する
書いてはいけないコメントはわかったら、次は書いたほうが良いコメントも理解しましょう。
書いたほうが良いコメントは
「コードを書いているときにその情報が無いと、そのコードが理解できない大切な考え」
具体例
- このデータだとハッシュテーブルよりもバイナリツリーの方が40%速かった
- 左右の比較よりもハッシュの計算コストのほうが高い
- このクラスはfatしてきているので、サブクラスを作って整理したほうが良い
- TODOコメント
これらのコメントはなぜそのようなコードにしたのかと言うのが書かれていて、
読み手はコメントがないとわからない大切な情報です。
読み手の立場になって何が必要になるかを考える
これは上記に書かれた部分と重複しますが、コメント必要かどうかは読み手が決めるものなので、
「常に仕様を理解していない他の人がこのコードを読んだときに、理解できるのか?」
というのを自問自答すると良いです。
今ならMENTA内で利用できる500円クーポンがもらえる
プログラムコードのコメントは正確で簡潔に
コメントは画面の量も取られる上、読むのにも時間がかかるので簡潔に書かなければならない
例えば
ポイント
「データをキャッシュに入れる。但し先にそのサイズをチェックする」
これだと「そのサイズ」というのがキャッシュを指すのか、データを指すのかわからない。
正しくは
「データをキャッシュに入れる。但し先にデータサイズのチェックをする」
というのが良い。
もう一つ例を出すと
「このファイルに含まれる行数をカウントして返す」
というコメントはあまり良くない。
理由としては
- 空行はカウントするのか?
- "Hello\n"は1行か?2行なのか?
- 改行コードは何でも良いのか?
などが考えられる。
正すとすると
「このファイルに含まれる改行文字('\n')を数える」
この様になる。
こうすると空行も含まれることがわかり、またキャリッジリターン(\r)は含まれない事がわかる。
制御フローを読みやすくする
条件やループなどの制御フローはできるだけ「自然」にする。コードの読み手が立ち止まったり読み返したりしないように書く。
if文
if (10 <= length)
よりも
if (length >= 10)
の方が読みやすい。
この書き方は英語の用法にあっている。
英語で「もし君が1年間で10万ドル以上稼ぐならば」や「もし君が18歳以上ならば」というのは自然です。
しかし「もし18年が君の年齢以下ならば」というのは不自然ですね。
まとめると
ポイント
調査対象の値が左辺に来るのが望ましい
またif文は否定形よりも肯定形を使うほうが解釈しやすいです。
三項演算子
if文を使う際単純な場合はif文よりも三項演算子を使うこと。
その方が行数も短く、冗長にならずに済む。
ただし行数を短くする目的で若干複雑なif文を三項演算子にしてしまうと、
他の人が読みづらいコードになってしまうため避けること。
他にも
- goto文はコードの可読性が下がるため避けること
- ネストを浅くする
などのことが書かれています。
そちらも重要なので詳しく例を知りたい方は本書で確認ください。
巨大な式を分割する
巨大な式は飲み込みやすい大きさに分割する
巨大な式を分割するには?
- 説明変数を使う
- 要約変数を使う
説明変数とは例えば
if line.split(':')[0].strip() == 'root'
というコードを説明変数を使うと
username = line.split(':')[0].strip() if username == 'root'
という風に置き換えられる。
コード行数は2行になってしまったが、意味不明だった左辺が変数名をつけることで、
何を表している変数なのか、ぐっと理解しやすくなりました。
要約変数は
final boolean user_owns_document = (request.user.id == document.owner_id)
条件式を変数に要約することで、
どういう条件なのかがパット見て理解できるようになりました。
ポイント
今回紹介した、説明変数、要約変数を使う
他にもここに記載していない内容について触れたい方は本書で確認ください。
変数の読みやすさ
前の章では分かりづらいコードは変数にして説明を付与することを勧めたがここではその逆で、
変数を削除したり、スコープを縮めることで、変数を読みやすくします。
変数を読みやすくするポイント
- 役に立たない一時変数を削除
- 変数のスコープを縮める
- 変数は基本的にイミュータブルにする
を行うと大分コードが読みやすくなります。
役に立たない一時変数削除
now = datetime.datetime.now();
root_message.last_view_time = now;
このnowという一時変数は説明変数になっていない(説明変数を使わなくてもわかる)ので、
一時変数は使用しないほうがわかりやすくなります。
変数のスコープを縮める
変数を利用する際に、あまりにも遠くに変数が定義してあると、
コードを読む際にまた変数定義を見に行って、読みたいコードに戻って。。。
とかなり可読性が下がる。
できるだけその変数を利用する際に変数を定義すること。
変数は基本イミュータブルにする
同じ変数を操作する場所があちこちにあると、現在変数の値がわからなくなります。
なるべく変数はイミュータブルにすべきです。
まとめ
ポイント
- 役に立たない一時変数を削除
- 変数のスコープを縮める
- 変数は基本的にイミュータブルにする
この3つは並のエンジニアであれば出来なければ行けないです。
他にもいくつか触れてないことが本書で書かれています、
この基本的な章はエンジニアとして大事なので抑えておきましょう。
一度に一つのことだけをするように実装する
コードは一つずつタスクを行うようにする
「巨大な式を分割する」という章と少しにていますが、
一つの関数では一つのタスクのみ行うことを心がけましょう。
一つの関数で複数のことをやりすぎると巨大な式になってしまい、可読性落ちたりテストが書きにくくなります。
今ならMENTA内で利用できる500円クーポンがもらえる
ではどのように一つの関数で一つのタスクを行うのか?
巨大なタスクを分割する流れ
- コードが行っているタスクを全て列挙する。このタスクという言葉はゆるく使っている。「オブジェクトが妥当かどうかを確認する」のような小さいこともあれば、「ツリーの全てのノードをいてレートする」のように曖昧なこともある
- タスクをできるだけ異なる関数に分割する。少なくとも異なる領域に分割する
この手順に沿って巨大なタスクを分割すると良いです。
例えば
(例)仕様
- 「都市」を選ぶときには「LocationName」→「SubAdministrativeAreaName」→「AdministrativeAreaName」の順番で使用可能なものを使う
- 以上の3つが使えない場合は「都市」に「Middle-of-Nowhere」というデフォルト値を設定する
- 「国」に「CountryName」が使えない場合は「Planet Earth」というデフォルト値を設定する
の仕様をそのまま実装すると
var place = location_info["LocationName"];
if (!place) {
place = location_info["SubAdminitrativeAreaName"];
}
if (!place) {
place = location_info["AdministrativeAreaName"];
}
if (!place) {
place = "Middle-of-Nowhere";
}
if (location_info["CountryName"]) {
place += "," + location_info["CountryName"];
} else {
place += ",Planet Earth";
}
のようになる。
ポイント
- location_infoディクショナリから値を抽出する
- 「都市」の優先順位を調べる。なかったらデフォルトで「Middle-of-Nowhere」にする
- 「国」を取得する。なければ「Place Earth」にする
- placeを更新する
このようにタスクが分解できます。
このタスクごとに関数を実装すれば可読性があがります。
初心者の頃はこの分割が出来ずに大きな関数を作りがちになります。
徐々に分割出来るように癖をつけて日々実装してやるのが良いです。
コードに思いを込める
これは「巨大な式の分割をする」、「一度に一つのことだけをするように実装する」のまとめのような章になる。
つまり
「おばあちゃんでもわかるように説明できなければならない」 by アルベルト・アインシュタイン
プログラムでもこれを体現する必要があるということです。
本書では以下の手順を示している。
この手順で巨大な式を分割していく
- コードの動作を簡単な言葉で同僚にもわかるように説明する
- その説明の中で使っているキーワードやフレーズに注目する
- その説明に合わせてコードを書く
リーダブルコードまとめ
リーダブルコードについて現役中堅エンジニアとして重要な部分をまとめました。
何度も言いますがWebエンジニアにとっては必ず持っているべき本の一つです。手元に置いておくことをオススメします
この本をある程度実践体現できるだけで、大分可読性の良いコードを書けるようになります。
また実際この本書の内容を全て体現できるエンジニアは稀有です。
なのでこの本書を体現出来るように、
一度読んで終わりではなく、日々エンジニアとしてコードを書いていく中で、
綺麗に収まるように書く方法がわからなくなったときや、複雑なコードを書く前など
何度も読み返して自分の中に定着していくようにすることをオススメします。
今ならMENTA内で利用できる500円クーポンがもらえる