まずは蝋の翼から。

学んだことを書きながら確認・整理するためのメモブログ。こういうことなのかな?といったことをふわっと書いたりしていますが、理解が浅いゆえに的はずれなことも多々あると思うのでツッコミ歓迎

リーダブルコード俺俺メモ①第一部 表面上の改善

最近コードを量産することが多いので、リーダブルコードを読む。

コードは今まで我流だったし、コードを書く機会がそこまで多くなかったので会社でレビューを受けた回数も少ないため一回ちゃんとしないとなぁというのがモチベーション。
自己認識としては、変数名がやばい(後で読み返したとき、自分でも一瞬なんだっけこれ?ってなる)。

完全に俺俺メモなので、各章毎に個人的にできてないことや意識しておくことのみをメモ書き程度の書き方で羅列する。適宜自分なりの解釈や、記事などから引用した内容も書くためリーダブルコード自体には書いてないこと(注釈で「自己解釈」と書いている行)も入るので注意。

また、プログラマーではないので、あまり使わない部分もあるため節によっては飛ばしている。

1章. 理解しやすいコード

読みやすさの基本定理

コードは他の人が最短時間で理解できるように書かなければいけない。「他の人」というのは少し時間が経ったときの自分も含める。 例えば、そのコードを別のときに流用することもあるし、何かしらの修正が発生したときに改めて読む必要があるが、そのときにできるだけ思い出す時間をかけないためにも必要となる。 また、「理解できる」というのは、他の人が変更を加えたりバグを見つけることができるレベルを指す。

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

変数名や関数名などの「名前」は短いコメントだと考える。つまり、良い名前は多くの情報を伝えることができるので名前に情報を詰め込む必要がある。

明確な単語を選ぶ

抽象的な単語は避ける。

例えば、getは何を取得してくるかわからないが、fetchdownloadなら何かしらのデータを外部から取ってくるニュアンスが伝わる。
他には、sizeは何のサイズかわからんので、heigh, numbersなどをつける。

また、stopkillpouseの方がより細かい停止のニュアンスがわかる。

tmpやretvalなど汎用的な名前を避ける

tmphogeretval(return valueの略)といった特に意味がない単語は避けて、より明確な単語にする。

明確な単語となることで、その部分で何がしたいかが明確になるのでバグなどを見つけやすくなる。

コメントでいいのでは?と思うかもしれないが、多くの場合コメントは宣言時に添えるため宣言時以降で使用する場合はそのコメントを読まない。そのため、明確な名前がついていると宣言時以降でも間違いや勘違いをした使い方を避けることができる*1

ただし、tmpに関しては例えば変数の入れ替え時など「この変数は他に役割がない(一時的な値の退避など、直後の1回こっきりでしか使わない一時的なもの)」という情報、つまりtmpという単語の意味自体が意味がある場合などは使用してもよい。

# python
# bがaより大きい場合、aとbを入れ替える
# 変数上書きが発生するので、一時的に値を退避するためにtmpを使う
if a < b 
  tmp = a
  a = b
  b = a

ただし、そのような機会は少ないしつい甘えた使い方をしそうなのでtmpを使用したときは「本当にtmpでいいか?」と自問しよう*2

イテレータ

i,j,kなどはそのものがイテレータだという意味になるので使用してもよい。

ただし、例えばi,j,kが同じループ内で入り乱れているときはどのときにijkかを一見して判別するのが難しい。つまり、 iと間違えてjを使っていても気づきづらい

そのため、例えばmemberを表すのがi、clubを表すのがjならば、member_i, club_i (どちらもiに統一)にするとより良い(記事中では略したmi,ciも勧めているがmってなんだっけ?ってなったりmember motherで頭文字が被ることがあとでわかったときに書き直しが生まれそうなので使わない方がいいと思う)。

本書では書いていないが、例えばmembersリストから中身を取り出すイテレーションのときにiではなくmemberを使っているコードをよく見る。これは、memberイテレータなのかどうかパッと見でわからないからあまり良くない、ということになる?*3

# python

# i, jを使う(△)
for i in members
  for j in clubs
    print(i)
    print(j)

# sを除外した単語を使う(△)
for member in members
  for club in clubs
    print(member)
    print(club)


# 明示的なイテレータを使う(◎)
for member_i in members
  for club_i in clubs
    print(member_i)
    print(club_i)

抽象的な名前を避ける

例えば、ServerCanStartの場合、サーバーの何かをスタートできるようにする関数ということはわかるが何をStartできるかがよくわからない
そのため、CanListenOnPortに変えることで「PortをListenできるようにStartする」ことが明示される。

重要な情報を加える

知らせなければ使い方を誤りやすいような場合は、情報に追加する。フォーマットや時間(sec?ms?)、状態(変換前?後?)などは誤りやすい。

例えば、idのフォーマットがhex(16進法)ならばhex_id、時間の単位がms(ミリセコンド)で入っている(別の単位だと勘違いしたときn倍大きい数値になってしまう)ならduration_msとする、など。

ただし、必ず付けるのではなく、間違った場合にバグが起きそうな場合のみ付ける。

名前の長さ

名前はある程度長くてもコード補完があるのであまり問題にならない。

また、単語の省略は人によってわからないことがあるので、一般的なもの以外は省略しない方が無難。例えば、manageをmngは一般的ではないが、stringをstr、documentをdoc、evalutationnをevalは一般的。

命名規則

エンティティ毎で記法を変えることで、エンティティ情報を伝える。例えば、クラス名はキャメルケース(CamelCase)、変数名はスネークケース(snake_case)、定数の変数は全て大文字CONSTANT_NAME、クラスのメンバ変数は接尾に_を入れるなど。このあたりは会社や言語によって変わったりより詳細定義があるので注意。

誤解されない名前

常にその名前が誤解されないか意識する

例えば、filterは除外されたのか選択されたのかわからないので、前者ならselect後者ならexcludeをつけたほうが明確。

他にも、clip(切り抜き)remove(削除) or truncate(切り捨て))、 length(長さ)max_length(最大の長さ) or min_length(最小の長さ) など。

範囲

条件を指定するときの範囲は未満(<)なのか以下(<=)なのかは統一する。基本的にはmin maxがついた名前とセットで使うと思うので、以上/以下(<=, >=)を使う*4

また、文脈によっては限界値となるmin maxではなく、包括的な意味のfirst lastbegin endなどでも良い。なお、starに対応するendは「超えたら終わる」のか、「超える手前で終わる」のか曖昧なので使わない。

bool値

true, falseの意味を明確にしないといけない。

例えば、xxx_flgに対するよくある批判として**「xxxになったときにフラグが立つのか、xxxするため(xxxする必要がある)のフラグ(xxxでないときにフラグが立つ)のかわからんから使うな」というのがある。例えば、start_flgは「スタートしてたら1」「スタートする必要があると1」どちらでも解釈できる。*5
そのため、is_xxx has_xxx can_xxx should_xxx などをつけると明確になる。

4章. 美しさ

コードを読みやすくするには余白・配置・順序を意識する必要がある

原則としては以下の3つがある。

  • 読み手が慣れているパターンと一貫性のあるレイアウトを使う
  • 似ているコードは似ているように見せる
  • 関連するコードをまとめてブロックにする

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

コメントの目的は正確には「コードの動作を説明する」ではなく、「書き手の意図を読み手に知らせる」ことである。

コメントをするべきではないこと

コメントは画面を占拠するので、占拠するだけの価値をもたせる。つまり、コードからすぐにわかることはコメントをしない必要がある。ポイントは「すぐに」。
例えば、以下のようなコードはコードを読めばコメントの内容は「すぐに」わかる。

# python
# 2番目の`*`以降を全て削除
name = '*'.join(line.split('*')[:2]

コードの処理をコメントしているだけのような「コメントのためのコメント」をしない。例えば、n = xxx.method(a,b)に対するコメントで「オブジェクトxxxに引数a,bを入れたメソッドZを適用してhogeな処理をしてnに格納する」はコードを記述しているだけ。
「価値のあるコメント」としては、「fugaな処理(処理内容の意図)をおこなう」「もしaがxxxなら、piyoが起き、違う場合puyoが返る(渡す値の違いによる例示)」のようにするべき。

コメントは不適切な名前の穴埋めではない。つまり、適切な変数名をつけていれば、コメントの多くは不必要となる。
そのため、名前に表せれない注意点や細かい部分などをコメントにする(次に見出しで詳細)。

自分の考えを記録する

優れたコメントは考えを記録するためにある。

そのコードに対して試したこと(最適化など)や、何故それをおこなうか(何故必要か)、足りてない部分(コード未整理や足りない機能など)といったことを書くことで、他の人がそのコードを読むときに理解が進み、無駄なことを考えなくてすむ。

また、定数にコメントをつけることでなぜその値を定数にしているか という、値の背景がわかるため、考慮漏れなどを防ぐことができる。

読み手の立場になって考える

プロジェクトに熟知していない人にもわかるように書く。

質問されそうなことを想像する

何故このコードを書いたか背景がわかりづらい部分は読者が疑問に思う場合が多い。

例えば、別の簡単なやり方がありそうなのに、何故このような方法で行っているかなど

ハマりそうな罠を告知する

このコードを読んで勘違いしそうなこと、コードを間違えて使いそうなこと、使用時に考慮しなければいけないこと はなにか。これらを前もって予告することで、罠を回避することができる。

例えば、処理に時間がかかる部分や、特定条件では使用できない、など。

全体像へのコメント

新しくプロジェクトに参加した人にとって、全体像を理解することが最も難しい。

そのため、そのコードファイルに対して「どのようなことをおこなうためのコードが書いたファイルなのか」という全体像を記述するとファイルの立ち位置への理解が進む*6

また、これはファイル単位だけでなく関数やClass単位、それぞれの塊単位でも同様に「どのようなことをおこなうための関数なのか」を書くと良い。このあたりは、各有名ライブラリのソースコードを読むと関数やClassへの要約コメントの書き方の勉強になる*7

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

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

曖昧な代名詞を避ける

これ、それ、あれなどの代名詞は指す言葉が曖昧になるし、どれを指すか解釈しなければいけないのでできるだけ使わない。

入出力のコーナーケースに実例を使う

入力(引数)や出力(返り値)がどういうときにどうなるか、実例を交える。

*1:自己解釈

*2:自己解釈

*3:自己解釈

*4:自己解釈

*5:自己解釈

*6:自己解釈

*7:自己解釈