MENU

『The Art of Readable Code』まとめ&感想

はじめに

最近リーダブルコードを読んでいるのですが、読んでる最中は「なるほど~」という感じですが、時間経つと普通に忘れそうなのでブログでまとめることにしました。

  • 公開してますが、実質自分用メモです。

  • 色んなTipsが書かれていますが、自分が使っている言語・職場のコード規約的にあんまり使うことなさそうな部分はすっ飛ばしていたりします。

  • 英語の勉強を兼ねて英語版を読んでるので、一部正確に意味を捉えられてないところがあるかもしれません。

全体読んだ感想

  • タイトルの通りで「読みやすいコード」とは何か・読みやすいコードにするにはどうすればいいかといったことが書かれている

    • 読みやすいコードは、読み手が最短の時間で読めるコード
    • 読み手が書き手の考えをコードから読み取れるコード
  • 色んな手法が紹介されているが、抽象度高くして捉えると一貫して下記の様なメッセージが込められているのかなと感じた

    1. 自分が何をやりたいのか(どんな処理を書きたいのか)正確に捉えよう
    2. 1で正確に捉えたものを、読み手にも正確に伝わるようにコード化しよう
      • 誰かに説明するのと同じような流れで処理を書く
      • 読み手に正しく伝わるよう(メソッドや変数を)命名する
      • コードから読み取れないことをコメントで捕捉する
  • 改修と機能追加を繰り返す中で意味不明なほどに複雑になったコード、大して複雑でもないのになぜか読み解くのに時間のかかるコードってよくあると思うけど、それらがなんで読みづらいのか、そうならないためにはどうすればいいのかがよく理解できた。

Part 1 見た目レベルでコードを改良する

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

  • 意味が明確な単語を使う
    • 文脈に応じて、 Get などの代わりに FetchDownload といったコードを使い
  • 汎用的な名称は避ける
    • 特に理由が無ければ、 tmpretval といった名前は使わない
  • 具体的な名前を使う
    • ServerCanStart() などの不明瞭な名前よりも、 CanListenOnPort() の方が処理を詳細に説明してくれる
  • 変数名に重要な追加情報をくっつける
    • その変数に何が入るのか(2進数、8進数)だったり、どんな単位の数(秒、キロ等)が入るのか等
  • スコープが大きいのであれば長い名前を使う
    • 長く使う変数を短い名前で済ませない(必要な情報を詰め込む)
  • 大文字・アンダースコア等を意味のある方法で使いこなす
    • クラス・メソッド・定数変数が一目でわかるような使い分けをするとコードが読みやすくなる

3章 誤解を招かない名前

  • 最良の名前は、誤解を招かない名前。
  • あなたのコードを読む人があなたと同じように理解するとは限らない。名前を決める前に誤解が起こりえないか想像してみる。
  • ある値の上限値・下限値を定義したい時は max_min_ の接頭語を付けると良い。
  • 包括的範囲(起点と終点どちらも範囲に含まれる)を示すときには、firstlast が良い。
  • 起点が範囲に含まれ、終点が範囲に含まれないような時は beginend が適している。
  • boolean型は、 ishas を使うと意味が明白になる。否定語は避ける(ex. disable_ssl)
  • get()size() などの名前は変数の値を取ってくるなどの軽い処理に使う名称と広く認識されているので、それ以外の用途では使用しない。

4章 美学

  • コードは首尾一貫した意味のあるフォーマットがなされるだけで、読みやすいものになる。
    • 似通った処理を行うコードブロックは、見た目も同じようにしてしまう。
    • あるコードを A, B, C という順番で参照したなら、別の場所でも同じ順番で参照する( B, C, A などと順番を変えない)
    • コードをパラグラフに分けるために空行を活用する

5章 何をコメントすればいいのか知る

  • コメントを残す目的は、読み手か書き手と同じだけのことを知れるようにすること。そのために...
    • やるべきでないこと
      • コードからおのずと読み取れるコメントを残す
      • 正しくない名前をそのままにする
    • コメントを書く時に考えること
      • なぜこのようなコードになっているのかの見解を残す
      • TODO:FIXME: などを使って爪痕を残しておく
    • 読み手の立場で考える
      • 読み手の予想通りに動かない処理にはコメントを残す
      • スコープの広いコメントを残す。(システムの全体像・それに対してこのクラスがどんな役割を担っているのかなど)
      • そのブロックをようやくするようなコメントを残せば、読み手は細部に囚われて道を見失うことがなくなる。

6章 的確かつコンパクトにコメントする

  • コメントは専有するスペースに対して高密度な情報が入ってないといけない(少ない言葉にたくさんの情報を詰め込む)
    • itthis 等の代名詞は紛らわしいので避ける
    • 必要に応じて、 メソッドの インプット/アウトプット の具体例を挙げる
    • その処理について、コードを読めばわかる細部の話よりも、レベルの高い(抽象度の高い?)趣旨を説明する。
    • 一単語でより多くのことを説明できる専門用語を使う(ex. caching layer, Canonicalize

Part 2 ループとロジックをシンプルに

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

  • 左辺と右辺の値を比べる時、可変の値(調査の対象)となる値は左辺に、不変の値(比較対象)は右辺に置くとわかりやすい。

[例]

if ( list.size() > 10)
  • 参考演算子や do/while等は読みにくさに繋がりやすいので基本的には避ける。これらを利用することで簡潔かつ読みやすくなるケースにのみ利用する。
  • if/else の順番にも注意。一般的には ポジティブ/簡潔/関心をひく 条件を最初に持ってくるとよい。
  • ネストが深すぎると、読み手は頭の中で処理をスタックにpushしたりpopする必要があるので、読むのに集中力を要する。
    • 早期リターンはネストを浅くするのに有用。

8章 巨大な式を切り分ける

  • 巨大な式を分解することで、読み手が消化しやすい(読み込みやすい)コードになる。
    • 説明的変数を導入する。

[例]

(Before)

if (line.split(':')[0].strip().equals(root)) {
}

(After)

String username = line.split(':').strip();
if (username.equals(root)) {
}
  • ド・モルガンの法則を用いることで条件分岐がシンプルになる。

[例]

(Before)

if(!(a && !b))

(After)

(!a || b))
  • ロジックが難しいと感じるとき、真偽を反対に考えてみたり、問題へのアプローチを反対の方向からやってみるだけで処理がシンプルのなることがある。

9章 変数と可読性

  • 変数には、可読性について3つの課題がある
    • 変数が増えれば増えるほど、追い続けるのがむずかしくなる
    • 変数のスコープが広がるほど、より長く追い続けないといけなくなる
    • 変数の値が頻繁に変わるほど、現在の値が何なのか把握するのが難しくなる
  • その問題を解決するためには変数を少なくして"軽量"にすることが重要。そのために3つのアクションを取ることが出来る
    • 不要な変数は取り除く。
      • 中間値を入れるためだけの変数は無くても処理に問題ない
      • フローを制御するための変数は使わない(制御フロー変数は早期リターンで回避できる)
    • 変数のスコープを短くする。
      • グローバル変数を使わない
      • ある節の中でしか使わない変数は節の中で宣言する
    • 変数には一度しか代入しない。
      • const, final等の定数を活用する

Part 3 コードを再構築する

10章 無関係で副次的な問題を抜き出す

  • コードから、処理の主目的とは関係ない副次的な問題を定義して、切り離すことでコードはより読みやすくなる。
    • 例えば、ユーザー情報をURLに暗号化する処理 に対し、 pythonのオブジェクトをURLが許容できる文字列に暗号化する 処理というのは副次的なものである。
    • このような汎用的な処理は、既存のライブラリを活用するか、汎用コードとしてどんどん書いてしまおう。
    • 汎用コードはプロジェクトから完全に切り離すことができるし。テストも楽なので素晴らしい。
  • プロジェクト固有のコードから、汎用的な処理を行うコードを切り離してしまうことで小さなコアだけが残る。
    • こうすればプログラマーは、コンパクトでよく定義された問題だけに集中することができる。

11章 一度に一つのことをやる

  • コードを読みやすくするには、一度に一つのタスクだけを行うようにコードを構築すべき。
    • そのコードが行っているタスクをリストに書き出してみる
    • タスク毎に処理を分割する。関数化が容易であれば関数として切り出す。そうでなければひとつのパラグラフとしてまとめておく。

12章 考えをそのままコードにする

  • 複雑な考えを誰かに説明するには、アイデアを平易な言葉で説明するスキルが必要になる。それと同じく、読み手はプログラムが何をしているのかをソースコードから読み取るので、コードも平易な言葉で書かれる必要がある。
  • そのためには、下記の簡単なプロセスが役に立つ。
    • そのコードが何をする必要があるのか、同僚に話すように平語で説明してみる
      • どういうロジックなのかを平易な言葉で説明する
    • その説明で出てきたキーワード・キーフレーズに着目する
      • コードから副次的な問題を切り分けることにも役立つ
    • その説明の通りにコードを書いてみる
  • "平易な言葉で説明する"というのはコーディングの範囲外の話だが、ラバーダックに自分の問題を説明するだけで問題が解決することはよくある。プログラムやアイデアを言葉に落とし込むことは、課題や書くべきコードをはっきりさせることに役立つ。

13章 少ないコード量で済ませる

  • 最も読みやすいコードは何も書かれていないコード。
    • コードというものは、一度書かれれば全てテストされ、保守されなければならない。
    • コード量が多ければ多いほど、開発は難しくなる。
  • コードベースを出来うる限りコンパクトに・軽量にしておくには、
    • ユーティリティーコードをどんどん作って(使って)、重複したコードを取り除いてしまう。
      • 標準ライブラリにどのような関数があるのか、15分でもいいので目を通してみる。
    • 使っていないコード、無用な機能を削除する。
    • プロジェクトとサブプロジェクトを区分けして別々に管理する。
    • コードベースの 重さ を常に意識しておく。

厳選されたトピック

14章 テストと可読性

  • テストコードにおいても可読性というのは本当に重要で、テストコードが読みやすければ書きやすくもなるのでメンバーは気軽にテストを追加してくれるようになる。
  • テストしやすいようにコードを書けば、コード自体がよりよい構成になっていく。
  • テストコードをもっとよくするには下記の様なポイントがある。
    • 出来る限り簡潔なことが理想。テスト毎の インプット/アウトプットは一行で書けるようにするとよい。
    • テストが失敗した時は、バグの原因を追えるようなエラーメッセージを出力する。
    • テストの入力値は、可能な限り簡潔にする(テストケースが必要とする以上に複雑にしない)
    • テスト名に必要な情報を詰め込んでやると一目でわかりやすい。

    例) Test_<FunctionName>_<Situation>

15章 "分/時間カウンター" を設計・実装してみよう

  • 今までの話を踏まえた演習

参考

  • 『The Art of Readable Code』

今回初めて英語の技術書を読んでみましたが、リーダブルコードは内容自体が初心者向けなこともあって、表現も平易でかなり読みやすいです。

英語には苦手意識あるけど頑張りたいって人におすすめのレベル感だと思います。

さすがに日本語で読むよりは時間かかるけど、日本語だとさらっと読み流してしまう部分も英語だとしっかり読もうとするので逆にいいかも?と思いながら読んでます。