「リーダブルコード」を読み終えて 〜 技術書の備忘録
『リーダブルコード』を読み終えたので、特に重要だと思ったポイントを整理しておきます。
自分はサーバーサイド Ruby on Rails、フロントエンド Next.js (React) の技術スタックで開発しているので、日々の業務で使える形に落とし込んでまとめました。
本書のひとこと概要
「優れたコード」とは何かという定義を明確にし、誰かがそのコードを理解するまでにかかる時間を最短にするための、具体的で実践的なテクニックが学べる書籍です。
特に重要だと感じた章をピックアップ
「優れた」コードの定義
本書における読みやすさの基本定理は、「コードは他の人が最短時間で理解できるように書かなければいけない」という点に集約されます。
単に「コードが短い」ことが良いのではなく、たとえ行数が増えたとしても、読み手が立ち止まらずに理解できる「安心感」があるコードこそが優れたコードです。
Next.js での例:複雑な条件レンダリング
三項演算子を重ねるよりも、早期リターン(ガード節)を使って「何を表示しないか」を先に処理したほうが、メインのロジックが直感的に理解しやすくなります。
// ❌悪い例:簡潔だが、条件が増えると追いかけるのが大変
const UserProfile = ({ user, isLoading }) => {
return isLoading ? <Spinner /> : !user ? <Error /> : <Details user={user} />
};
// ⭕️良い例:読み手が最短時間で理解できる「安心感」のあるコード
const UserProfile = ({ user, isLoading }) => {
if (isLoading) return <Spinner /> // 早期リターンで例外を先に排除
if (!user) return <Error />
return <Details user={user} />
};
名前に情報を詰め込む / 誤解されない名前を付ける
- 明確な単語を選ぶ: get や data のような「空虚な単語」を避け、より明確で「カラフル」な単語を選ぶことで、関数の意図を正確に伝えられます。
- 単位を付加する: 時間を扱う変数なら delay ではなく delay_ms とする。
- 誤解を招く名前を避ける: filter() は「選択」なのか「除外」なのか曖昧なため、select() や exclude() を検討する。
- 関数(変数)名で意図を伝える:名前は「短いコメント」として機能させるべきです。名前だけで役割が分かるようにする。
JavaScript での例:関数名で意図を伝える
// ❌悪い例:短い名前をコメントで補足している
const calc = (subtotal, taxRate) => {
// 小計に税率を足した金額を返す
return subtotal + subtotal * taxRate;
};
// ⭕️良い例:関数名だけで「何を返すか」が伝わる
const calculateTotalWithTax = (subtotal, taxRate) => {
return subtotal + subtotal * taxRate;
};
JavaScript での例:誤解されない名前を付ける
// ❌悪い例:filter は「選択」か「除外」か分からない
const filterUsers = (users) => users.filter((user) => !user.isDeleted);
// ⭕️良い例:「除外」であることが明確
const excludeDeletedUsers = (users) => users.filter((user) => !user.isDeleted);
制御フローを読みやすくする
条件分岐やループなどの制御フローは、「自然」でなければいけません。
読み手がコードを読み返したり、頭の中に条件を記憶し続けたりする事態を避けます。
- 条件式の並び: 左側に「調査対象(変化する値)」、右側に「比較対象(安定した値)」を置く(例:if (age >= 18))。
- if/else の並び: 否定形よりも肯定形、単純な条件、あるいは関心を引く条件を先に書く。
- ネストを浅くする: 早期リターンを使って、直線的なコードを目指す。
Ruby on Rails での例:Controller 内での早期リターン
ネストが深いと、どの条件の中にいるのか把握するのが難しくなります。
# ❌悪い例:ネストが深く、条件を最後まで覚えておく必要がある
def update
if logged_in?
if current_user.admin?
# 更新処理...
else
flash[:error] = "権限がありません"
end
else
redirect_to login_path
end
end
# ⭕️良い例:ガード節でネストを削除
def update
return redirect_to login_path unless logged_in?
return flash[:error] = "権限がありません" unless current_user.admin?
# 更新処理(ここがメインロジックだと一目でわかる)
end
巨大な式を分割する / 一度に 1 つのことを
一度に複数のタスクを行うコードは理解を妨げます。
巨大な式は、意味のある単位で「説明変数」や「要約変数」に代入し、タスクを 1 つずつ片付けていくように構成します。
Next.js (React) での例:複雑なロジックの要約
// ❌悪い例:一度に複数の条件を評価しており、意図が読み取りにくい
return (
<div>
{user.id === document.ownerId && !document.isLocked && <EditButton />}
</div>
);
// ⭕️良い例:要約変数を使って「概念」を定義する
const canEdit = user.id === document.ownerId;
const isEditable = !document.isLocked;
const shouldShowEditButton = canEdit && isEditable;
return <div>{shouldShowEditButton && <EditButton />}</div>
無関係の下位問題を抽出する
「この関数が結局何をしたいのか(高レベルの目標)」を自問し、その目標に直接関係のない「下位問題」を別の関数に切り出します。
これにより、メインのロジックがシンプルになり、切り出された関数は再利用可能なユーティリティとして独立します。
Next.js での例:フォーマット処理の抽出
コンポーネントの本質的な役割から、データの整形ロジックを分離します。
// ❌悪い例:URLのクリーンアップ処理がコンポーネントに混ざっている
const ProfileLink = ({ websiteUrl }) => {
// 下位問題:URLの整形
const displayUrl = websiteUrl.replace(/^https?:\/\//, "").replace(/\/$/, "");
return <a href={websiteUrl}>{displayUrl}</a>
};
// ⭕️良い例:メインロジックが明確になり、整形処理は再利用可能になる。(下位問題は別ファイルに切り出して定義する)
const ProfileLink = ({ websiteUrl }) => {
return <a href={websiteUrl}>{formatUrlForDisplay(websiteUrl)}</a>
};
終わりに
『リーダブルコード』を読んで特に重要だと感じた点をまとめてきました。
Web アプリ開発はコードの量も膨大になるし、チームで開発することが基本になります。
チームメンバーのためはもちろん、未来の自分も助けることになるので、何度も手元で読み返して習得したいと思います。
Discussion