rhanda | 元銀行員Web系エンジニアの日記

実務未経験からWeb系受託開発企業に転職したひよっこエンジニアが覚えたことや日々の感情を残すブログ

『リーダブルコード』読みました

読んだきっかけ

研修としてのアプリケーション開発で沢山のコードレビューを頂くなかで、

「この書き方の方がぱっと見で何がしたいかわかりやすい」
「この変数名だと誤解を招く恐れがある」

といったアドバイスをたくさん頂きました。
「機能的に要件通り動作するかどうか」にしか未だ考えが及んでいなかった私は、よりコードのクオリティへの考えが必要だと感じ、ベースとなる知識を得るために本書を手に取りました。

構成

1章 理解しやすいコード
2章 名前に情報を詰め込む
3章 誤解されない名前
4章 美しさ
5章 コメントすべきことを知る
6章 コメントは正確で簡潔に
7章 制御フローを読みやすくする
8章 巨大な式を分割する
9章 変数と読みやすさ
10章 無関係の下位問題を抽出する
11章 一度に1つのことを
12章 コードに思いを込める
13章 短いコードを書く
14章 テストと読みやすさ
15章 「分/時間カウンタ」を設計・実装する



メモ

命名で相手に意図を伝える

最善の名前とは誤解されない名前である。
命名においては明確な単語を使い(Getではなく状況に応じてFetchDownloadを使うなど)、読み手が自分の意図を正しく理解できるかどうかを重視する。


コメントで意図を伝える

コードを読めばすぐにわかることをコメントで説明しない。
コードを読んだ人が「質問しよう」と思うところを予想してコメントをつける。

自分も開発の中で以下の様にコメントで補足してみました。
(動作確認用データを作る単純なrakeタスクですが、、、)

task tasks: :environment do
  puts "\n== 動作確認用のタスクを100件作成しています =="
  # seedで作った最初のユーザーと紐付けて作成する
  user = User.first
  raise "\n-- seedでユーザーデータを作成してから実行してください--" unless user

  # 適当に100件
  100.times do |n|
    user.tasks.create!(
      name: "タスク#{n}",
      created_at: Time.current + n.days,
      due_on: Time.zone.today + n.days,
      status: Task.statuses.keys.sample,
      priority: Task.priorities.keys.sample
    )
  end
  puts "\n== 動作確認用データ100件の作成が完了しました =="
end


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

行数を短くするよりも、他者が理解するのにかかる時間を短くする
こちらも自分の開発の中で取り入れてみました。
とても実効の流れが追いやすく、戻り値もわかりやすくなると感じました。

# 修正前
def current_user
  @current_user ||= User.find_by(id: session[:user_id]) if session[:user_id]
end
# 修正後
def current_user
  return unless session[:user_id]

  @current_user ||= User.find(session[:user_id])
end


オブジェクト思考プログラミング

データと操作をクラスという単位にグルーピングし、クラスに対応するオブジェクトと呼ばれるものを生成して利用するもの。


テストを整理する

整理するための指針
  • 新しいテストを簡単に追加できる様にする
  • 既存のテストをシンプルで理解しやすい状態に保つ
  • テストを見返す度、内容の把握に頭を悩ませずに済む様にする
良いコードを書いて保守しやすいテストを作るための武器
  • スペースの入れ方
  • 適切な命名
  • 重複の排除
テストを整理しておくことで得られる効果
  • バグの起きている箇所を特定しやすくなる
  • テストのパターンを見つけやすくなる
  • 漏れているテストケースを見つけやすくなる
助けになる2つの考え方
  • テスト対処を絞り分離すること。一度に多くの内容をテストしようとしないこと
  • にているテストをコンテキストによってまとめ、頭を使わずとも理解できる様にしておくこと


モックとは

自動テストで、実際のオブジェクトを置き換えて使うダミーのオブジェクトのこと。

常に本物のオブジェクトを使ってテストすれば良いのでは??

それは正しい考えで、本物を使えば本番と違う動作をしたりしないし、コードも読みやすく、今書いてある通りに動くため、テストのために特別な準備も必要ない。
しかし実体を呼び出すことが問題となる場合もあり(ユニットテストからの外部サービス呼び出しが高コストだったり遅かったりするケース etc)、モックを導入してオブジェクトを外から操作したり監視したりできる様にしておくと、テストのための設定や準備を柔軟に、また強力に行える様になる効果がある。


TDD(テスト駆動開発

プロダクションコードの追加より前にテストを書くというプラクティス。

TDDのステップ
  1. 失敗するテストを書く
  2. テストを成功させる
  3. リファクタリングする
TDDの利点
  • オーバーエンジニアリングを防ぐ
    • 最初に失敗するテストを書いて、必要性が示された時のみプロがクションコードを追加するため、過剰な作り込みを防止できる
  • 設計が改善され、テストされたコードになりやすい
    • 最初か設計とテストについて考えることになるため、保守しやすいシンプルさを保ち、読みやすいコードを生み出せる様になる
  • 複雑な内容を扱いやすい
    • 新しいプロジェクトや機能に取り組み始めたばかりの時は、確認すべきことやうまくいかないことが多く、疲弊することも多々ある。TDDでは一度に1つのテストだけに集中するため、問題を単純化し、目の前の課題にフォーカスできる。

新機能の追加時やバグを修正する時、実際に作業をする前に「何が成功なのか?」を常に考えることで、成功の定義が明確になり、スコープが限定され、本当に重要なことに集中できる。




まとめ

「読みやすいコードを書くこと」は機能的に優れたコードを作る。

本書では、より良いコードを書くための具体的な手法がいくつも示されていましたが、それに加えてその手段を使うための考え方が非常に勉強になったと感じました。

コードの可読性を上げることは、アプリケーションの機能とはそれ程深い関係がなく、+αのクオリティの様な問題だと思っていました。
しかし、

  • 読みやすいということは、レビューもしやすくなる。
  • 理解するまでの時間が短ければ、その後開発を進める上でも「ここはどうなってるんだっけ」の時間を減らすことができる。

といった様に、開発コストを削減できることを知りました。

加えて設計やテストのしやすさにも繋がって、最終的にはバグも少なく、機能的に優れたコードが出来上がるということを学びました。

どの階層・どのオブジェクトにこの仕事をやってもらい、この情報を知っている状況を作るのかということを鑑みた命名や設計ができる様、今後も勉強していきます。