新宿(近々代々木に移転)で働く18歳エンジニアのブログ

思ったこととか、技術的なこと書きます。

これを見るだけで読んだ気になれるリーダブルコードチェックリスト +α

名前に情報を詰め込む

  • [ ] 明確な単語を選べていますか?

    • 類語辞典
    • send, deliver, announce, distribute, route
    • find, search, extract, locate, recover
    • start, launch, create, begin, open
    • make, create, set up, build, generate, compose, add, new
  • [ ] 汎用的な名前を避けていますか?(あるいは使う状況を選ぶ)

    • tmp, retval, イテレータ(i,j,k) これらの汎用的な名前を使うときは。それ相応の理由を用意(一時的な保管が最も大切な場合など)
  • [ ] 抽象的な名前より具体的な名前を使えていますか?
  • [ ] 接尾辞や接頭辞を使って情報を追加していますか?

    • 時間やバイト数のように計測できるものであれば単位をつける
  • [ ] 名前の長さは短いですか?

    • 長い名前を避ける
    • スコープが小さければ短い名前でもいい
    • 頭文字と省略形
      • doc document
      • eval evaluation
      • str string
    • 不要な単語を投げ捨てる
      • 名前に含まれる単語を削除しても情報が全く損なわれないこともある

        • ConvertToString → ToString
        • DoServerLoop() → ServerLoop()
  • [ ] 名前のフォーマットで情報を伝えていますか?

    • クラスのメンバ変数にアンダースコアをつけてローカル変数と区別する

誤解されない名前

名前が他の意味と間違えられることはないだろうかと何度も自問自答する

  • [ ] より具体的な意味を持った動詞を使えていますか?

    • filter() → select, exclude
    • clip() → remove, trancate
  • [ ] 範囲の値の変数はそれで正しいですか?

    • min/max 限界値を明確にする場合(未満、以下)
    • first/last or begin/end

    Screen Shot 2017-06-02 at 14.57.32.png (177.5 kB)

  • [ ] 真偽値は以下の項目を満たしていますか?

    • [ ] 頭にis, has, can, shouldなどをつけてわかりやすくする
    • [ ] 否定形を避け肯定形にする

命名するときの手順

1.その関数、変数に関わる情報を日本語で考えて、ブレストする

2.それを英訳して、類語辞典githubで調べて適切な単語を抜き出す

3.その言語の命名規則にそって命名する

命名する前に読んだほうがいいリンク

コードのレイアウト

  • [ ] 一貫性のある簡潔な改行位置ですか?
  • [ ] メソッドを使って冗長な処理をまとめていますか?
  • [ ] たての線(列)はまっすぐですか?
hoge("tanakatanaka", "foo"     , "");
hoge("sato"        , "fugafuga", "");
hoge("matz"        , "bar"     , "phone");
hoge("dhh"         , "piyo"    , "jobs");
  • [ ] 意味のある順番にならべていますか?

    • 最重要
    • アルファベット順
    • 複雑なもの
  • [ ] 宣言はブロックにまとめられていますか?

    • 論理的なグループにわける
  • [ ] コードは似ている考えごとに段落に分割されていますか?

コメント

コメントの目的は書き手の意図を読み手に知らせること 基本的にはコードや、その名前でわかるべき。だが関数といして抽出していない処理などは何をやっているかわかりにくい場合があるので補足したほうがいいとは思う

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

  • [ ] コードからすぐわかることはコメントすべきでない
  • [ ] ひどい名前はコメントせずに名前を変えろ 優れたコード>ひどいコード+優れたコメント 
  • 自分の考えを記録する(exこのクラスは汚くなってきている)
  • コードの欠陥にコメントをつける

TODO: あとで手をつける FIXME: 既知の不具合があるコード HACK: あまり綺麗じゃない解決策 XXX: 危険大きな問題がある

  • 定数を定義する際には、その定数が何をするのか、なぜその値を持っているのかという背景が存在する場合が多いのでコメントをしよう
  • [ ] 質問されそうなことを想像する
  • ハマりそうな罠を告知する
  • 全体像についてコメントする
  • 処理についての要約のコメント
  • コメントは適当に書き出す→コメントを読んで改善が必要なものを見つける→改善の手順で書く。

コメントは正確で簡潔に

コメントはその書いた領域に対する情報の比率が高くないといけない

  • [ ] 複数のものを指す可能性がある「それ」や「これ」などのあいまいな代名詞を避ける
  • [ ] 歯切れの悪い文章を磨く
  • [ ] 関数の動作をより正確に具体的に記述する
  • [ ] 入出力のパターンを伝えることができる実例をコメントに書いておく
  • [ ] コードの意図を詳細レベルではなく、高レベルで記述する
  • [ ] よくわからない引数には、pythonでできるような名前付き引数コメントをつける
  • [ ] 情報密度の高い(多くの意味が詰め込まれた言葉や表現)を使ってコメントを簡潔に保つ

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

  • [ ] 左が調査対象(変化する)、右が比較対象(あまり変化しない)
  • [ ] 単純な条件を先に書く
  • [ ] 関心を引く条件や目立つ条件を先に書く
  • [ ] 条件は否定形より肯定形を使う
  • [ ] 否定形であっても単純で関心や注意を引く場合があるので、その場合はそっちを先に処理をする
  • [ ] 三項演算子はわかりにくくならないもの(単純な二つの値から一つを選ぶようなもの)なら使う
  • [ ] do-whileは使わない
  • [ ] 関数から早く返す(ネストを浅くするときも使うことができる)
  • [ ] if文などのループから返すのを内部で行うにはcontinueを使う
  • [ ] go toは使わない
  • [ ] ネストを浅くする

巨大な式を分割する

  • 説明変数(式を表す変数)を使う
username = line.split(':')[0].strip()
if username == "hoge"
  • 要約変数(大きなコードの塊を小さな名前に置き換えて、管理や把握を簡単にする変数)を使う
final boolean user_owns_document = (hoge == fuga);
  • ド・モルガンの法則を使う
not (a or b or c) <=> (not a) and (not b) and (not c)
not (a and b and c) <=> (not a) or (not b) or (not c)
  • 分かりにくい短絡評価は良くない
assert((!(bucket = FindBucket(key)))||!bucket->IsOccupied());

今回の場合以下の方がわかりやすい

bucket = FindBucket(key);
if (bucket != null) assert(!bucket->IsOccupied());
  • 複雑なロジックはhogeの反対を考えてみたり、否定したりして、もっと簡単で綺麗な書き方を見つける

コードが読みやすくならない変数の削除

変数の追跡が難しくなる、スコープが大きいと把握する時間が長くなる、変数の頻繁な変更に伴い現在の値の把握が難しくなる

  • [ ] 複雑な式を分割していない
  • [ ] より明確になっていない(変数にしなくても十分明確)
  • [ ] 一度しか使っていないので重複コードの削除になっていない
  • [ ] 中間結果を削除する
function hoge(array, value_to_remove){
    var index_to_remove = null;
    for (fugafuga) {
        if(array[i] === value_to_remove) {
            index_to_remove = i;
            break;
        }
    }
    if (index_to_remove !== null) {
        array.splice(index_to_remove, 1);
    }
}

関数から早く返すことで削除できる

function hoge(array, value_to_remove){
    var index_to_remove = null;
    for (fugafuga) {
        if(array[i] === value_to_remove) {
            array.splice(i, 1);
        }
    }
}
  • [ ] 制御フロー変数を削除する プログラムの実行を制御するためだけの変数であり、実際のプログラムに関係のあるデータは含まれていない
boolean done = false;
  • [ ] 変数のスコープを縮める

    • アクセス修飾詞を使う
    • 大きなクラスを小さなクラスに分割する
    • 最初から全ての変数を知る必要はないので、変数の定義は変数を使う直前に移動する(変数の数による)
  • [ ] 変数は一度だけ書き込む 変数を操作する場所が増えると、現在値の判断が難しくなる

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

プロジェクト固有のコードから汎用コードを分離するということ 1.関数やコードブロックを見て「このコードの高レベルの目標は何か ?」と自問する 2.コードの各行に対して「高レベルの目標に直接的に効果があるのか?あるいは無関係の下位の問題を解決しているのか」と自問する 3.無関係の下位問題を解決しているコードが相当量あれば、それらを抽出して別の関数にする

ただし、新しい関数をコードに追加すると、ごくわずかに読みにくさのコストが発生する。そことの兼ね合い。

一度に一つのことを

単一責任の原則

コードに思いを込める

1.コードの動作を簡単な言葉で同僚にもわかるように説明する 2.その説明の中で使っているキーワードやフレーズに注目する 3.その説明に合わせてコードを書く

  • [ ] ロジックを明確に説明する
  • [ ] ライブラリを知る
  • [ ] 解決策を言葉で説明する

プログラムを言語化(自然言語)することで、明確な形となる

短いコードを書く、コードを小さく保つ

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

  • 不必要な機能をプロダクトから削除する、過剰な機能はもたせない
  • [ ] もっとも簡単に問題を解決できるような要求を考える
  • [ ] 定期的に全てのAPIを読んで、標準ライブラリに慣れ親しんでおく

テスト

今後追加します

参考リンク

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)