🔖

リーダブルコード 自分用アウトプット

2025/01/13に公開

1章 理解しやすいコード

他者が最短で理解できるコードを書くのが理想(他者とは半年後の自分の可能性も)
読みやすく理解しやすいコードを書く

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

変数名や関数名は短いコメントのようなもの

ポイント

  • 明確な単語を選ぶ

    例)getPage→fetchPage,downloadPageなど

  • tmpなどの汎用的な名前を避ける

    一時的なデータの保持のような場合はOK

  • 抽象的な名前より具体的な名前を使う

    幅広く捉えられそうな意味の名前ではなく、ピンポイントで狭い意味の名前をつける。

    複数の処理を持つ関数なら処理ごとに分ける

  • 名前に情報を追加

    接頭辞などを追加

3章 誤解されない名前

他の意味と間違えられることはないか自問自答を繰り返す

  • 他者が見ても意図を正しく理解できるコードが最前

    filter, length, limitなどは曖昧な表現

    filter→select_~~~

  • 条件の境界を意識する

    max,minといった言葉を使う

    範囲はfirst,lastといった言葉を使う

  • bool値はこれから行うのかすでに行ったのか曖昧になりやすい

  • 意味的に正しくても一般的に認識されている意味が異なると誤解を生む

    一般的に認知されている言葉、慣習を意識

  • 複数候補を出して決める

4章 美しさ

コードには一貫性を持たせる(改行位置や見た目)

  • メソッドを使用し、重複をまとめる
  • 縦の線を揃える
    • コードの列を整理
  • 並び順は統一し、意味のある順番に並べる
    • 重要なもの順、アルファベット順など
  • コードを論理的なグループに分けてあげる
    • ハンドラ、ユーティリティ、ヘルパーなど
  • 空行を使ってコードを段落に分ける
    • 手順ごとなど

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

コメントの目的は、書き手の意図を読み手に知らせること

  • コメントすべきでないこと

    • コードからすぐにわかること

    • コメントのためのコメント

    • 名前の埋め合わせ

      →コメントで補助するのではなく、名前(変数名等)を変える

  • 自分の意図を記録する

    • 比較結果など処理方法を選んだ意図
    • Todoなど、コードの欠陥
    • 定数の背景(なぜその値なのか)
  • 読み手の立場で考える

    • 質問されそうなことを考える

    • ハマりそうな罠を告知

      関数を間違えて使ってしまう可能性などを考慮

    • 全体像のコメント

      簡単に何のためのコードか説明

    • 関数の要約

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

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

  • コメントは簡潔にする

  • 曖昧な代名詞を避ける

    • それ・これなど
  • 歯切れの悪い文章を磨く

    • 簡潔に直接的な文章にする
  • 関数の動作を正確に記述

    例)行数を数える→改行文字(\n)を数える

  • 入出力の実例を書く

    • 関数の全ての機能を見せるような実例(簡単な実例では無意味)
  • コードの意図を書く

    • コードそのままの説明ではなく、意図している処理などを書く
  • 名前付き引数コメント

    • 引数の値が何の値なのか名前をつけて説明
  • 情報密度の高い言葉を使用

    • 正規化するやブルートフォースなど多くの意味を含んだ言葉や表現を使用し、コメントを簡潔に

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

条件やループなどの制御フローはできるだけ「自然」にする。コードの読み手が立ち止まったり読み返したりしないように書く。

  • 条件式の引数は左に調査対象(値が変化する)の式、右に比較対象(値があまり変化しない)の式を記述

  • if/elseブロックの並び順

    • 条件は否定系より肯定系を使う
    • 単純な条件を先に書く
    • 関心を引く条件や目立つ条件を先に書く
  • 三項演算子

    行数を短くするよりも、他の人が理解するのにかかる時間を短くする。

    基本的にはif/elseを使う。三項演算子は使うことで簡潔になる時にだけ使う。

  • do/whileループを避ける

  • 関数から早く返す

    • return文を早く返すことは良いこと。むしろ望ましい時もある
  • gotoはC言語以外ほとんど必要ない。

  • ネストを浅くする

    • ネストは深くなればなるほど理解しづらくなる
    • 後から変更していくことでネストが増えていくため、変更する時には常にコードを新鮮な目で見て、1歩下がって全体を見る。
    • 失敗ケースをできるだけ早めに関数から返すことでネストを削除
    • ループ内部のネストはcontinueを使用

8章 巨大な式を分割する

巨大な式は飲み込みやすい大きさに分割する

  • 説明変数や要約変数を使用
    • 説明変数:式を表す変数。
    • 要約変数:大きなコードの塊を名前に置き換えて、管理や把握を簡単にする変数。
  • ド・モルガンの法則を使用
    • not(a or b) = (not a) and (not b)
    • not(a and b) = (not a) or (not b)
    • この法則を使用し、論理式を読みやすくする
  • 無理やりコードを1行にまとめると逆に理解しづらくなる。
  • 愚直にロジックを組むのではなく別視点から解決できないか考える
  • 同じ式は要約変数として関数上部に抽出
    • タイプミスを減らすのに役立つ
    • 横幅が縮まるので読みやすくなる
    • クラス名の変更が1箇所ですむ

9章 変数と読みやすさ

変数は多すぎてもプログラムが理解しづらくなる。

理由

1、変数が多いと変数を追跡するのが難しくなる

2、変数のスコープが大きいとスコープを把握する時間が長くなる

3、変数が頻繁に変更されると現在の値の把握が難しくなる

  • 変数の削除

    • 役に立たない一時変数
    • 中間結果
    • 制御フロー変数(プログラムの実行を制御するためだけの変数)

    上記のような変数は削除する

  • 変数のスコープを縮める

    • 変数を参照できるコード行数をできるだけ減らす

      →スコープが広いと考えなければいけないことが増える

    • JSの場合クロージャで包んだりして、プライベート変数を作る

    • JSでは変数宣言をつけないとグローバルスコープの変数になってしまう

    • 定義の位置を下げる

  • 変数を操作する場所が増えると、現在の値の判断が難しくなるため、変数はできるだけ変更しない(constなどを使う)

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

プロジェクト固有のコードから汎用コードを分離する

1、関数やコードブロックを見て「このコードの高レベルの目標は何か?」と自問する

2、コードの各行に対して「高レベルの目標に直接的に効果があるのか?無関係の下位問題を解決しているのか?」と自問する

3、無関係の下位問題を解決しているコードが相当量あれば、それらを抽出して別の関数にする

  • ユーティリティコード

    無関係の下位問題例

    • 文字列の操作
    • ハッシュテーブルの使用
    • ファイルの読み書き

    →これらの基本ユーティリティはプログラミング言語のライブラリとして実装されているが、たまに、自分で実装しないといけないことがある。

    その場合、関数として自分で書けば、複数プロジェクトで使えるユーティリティコードになる。

  • 汎用コードを抽出するメリット

    • 複数プロジェクトで使いまわせる
    • コードが独立することで改善が楽になる
  • プロジェクトに特化した機能でも、大きなコードから取り除くだけでコードが読みやすくなり効果がある

  • やりすぎて小さな関数を作りすぎると、あちこち飛び回ることが増え、逆に読みづらくなるので注意

11章 一度に1つのことを

コードは1つずつタスクを行うようにしなければいけない

1、コードが行っているタスクをすべて列挙する。

2、タスクをできるだけ異なる関数に分割する。少なくとも異なる領域に分割する。

  • タスクは小さくできる
  • オブジェクトから値を抽出する
  • タスクをどのように分割するかより、分割するということが大事

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

知識が少ない人が理解できるような「簡単な言葉」でコードを書くべき。

1、コードの動作を簡単な言葉で同僚にわかるように説明する

2、その説明で使っているキーワードやフレーズに注目する

3、その説明に合わせてコードを書く

  • 簡単なコードを書くためにはライブラリが何を提供してくれるかを知る
  • プログラムを簡単な言葉で説明することで、分割するべき下位問題が見つかる。逆に説明できなければ何かを見落としていると気づける。

13章 短いコードを書く

最も読みやすいコードは何も書かれていないコード

  • プログラマは実装にかかる労力を過小評価してしまう。そのため、プロジェクトに欠かせない機能を過剰に見積もる。

    →結果、多くの機能が完成しないか、全く使われないか、アプリケーションを複雑にする

  • 質問と要求の分割

    • 要求を詳しく調べれば問題をもっと簡単にできることもある
    • 「要求の削除」と「より単純な問題の解決」に利点がある
  • コードを小さく保つ

    プロジェクトが進むと、複雑化していき、バグも見つけづらく、全てを把握できなくなっていく。

    →誰も触りたくなく、コードを扱うのが厄介になるという悪循環

    プロジェクトが成長してもコードをできるだけ小さく軽量に維持する

    • 汎用的なユーティリティコードを作成し、重複を削除する
    • 未使用のコードや無用の機能を削除する
    • プロジェクトをサブプロジェクトに分割する
    • コードの重量を意識する。軽量で機敏にしておく。
  • ライブラリに親しむ

    • たまにライブラリを調べる習慣を受ける
    • どんなことができるかをおおまかに感じ取る
  • 不必要な機能をプロダクトから削除する。過剰な機能は持たせない。

  • 最も簡単に問題を解決できるような要求を考える

  • 定期的にすべてのAPIを読んで、標準ライブラリに慣れ親しんでおく

14章 テストと読みやすさ

  • テストを読みやすく保守しやすいものにする

    テストコードの読みやすさはコードの読みやすさと同じくらい大切。

    他のプログラマが安心してテストの追加や変更ができるように、テストコードを読みやすくする。

    仮に、テストコードが巨大で読みにくいと、

    • 本物のコードを修正するのを恐れる
    • 新しいコードを書いた時にテストを追加しなくなる
  • テストを読みやすくする

    • 大切でない詳細はユーザから隠し、大切な詳細は目立たせる

    • 最小のテストを作る

      テストの本質は「こういう状況と入力から、こういう振る舞いと出力を期待する」」のレベルに要約できる。→1行でまとめられることが多い

    • 独自の「ミニ言語」を実装する

      • ヘルパー関数を作る
  • エラーメッセージを読みやすくする

    エラーメッセージはできるだけ役立つようにする

  • テストの適切な入力値を選択する

    コードを完全にテストする最も単純な入力値の組み合わせを選択しなければいけない

    • 入力値を単純化する

      テストには最も綺麗で単純な値を選ぶ

    • 1つの機能に複数のテスト

      完璧な入力値を1つ作成<小さなテストを複数作成

  • テストの機能に名前をつける

    例)test_<関数名>

    • 他のコードから呼び出されることはないので名前が多少長くなっても良い
  • テストに優しい開発

    テストしやすいコード

    • 明確なインターフェースがある
    • 状態や「セットアップ」がない
    • 検査するデータが隠されていない
    • プログラムを疎結合にする

    あとでテストを書くつもりでコードを書くとテストしやすいコードを設計するようになる

  • やりすぎ注意

    • テストのために本物のコードの読みやすさを犠牲にしない
    • テストのカバレッジは90%ほどで良い
    • テストがプロダクト開発の邪魔になる

まとめ

  • テストのトップレベルはできるだけ簡潔にする。入出力のテストはコード1行でできると良い
  • テストが失敗したらバグの発見や修正がしやすいようなエラーメッセージを表示する
  • テストに有効な最も単純な入力値を使う
  • テスト関数に説明的な名前をつけて、何をテストしているのかを明らかにする。

15章 「分/時間カウンタ」を設計・実装する

  • 素朴な解決策から始める

    • 設計上の課題を見つける

      →例)速度とメモリ

  • 「ベルトコンベヤー」を設計

    →速度とメモリの課題は解決

    →高パフォーマンスのアプリに適していない、柔軟性が乏しい

  • 最終的な設計

    • 複数の下位問題に分割

      →上記の問題解決

      ボトムアップで3つのクラスを作り、それぞれで各下位問題を解決

Discussion