注意すべきことを、なるべく具体的に列挙しました。
現代ではIDEが指摘してくれる事項
以下のようなものは、優秀なツールだと警告が出るので自然に直りますが、気をつけましょう。
冗長な変数
一箇所からしか呼ばれない関数
複数箇所であっても常に同じ値が入る引数
どこからも呼び出しのない関数
冗長なif
到達不能コード(引数が定数になってしまっている結果到達不能になる場合も含む)
変なインデント
typo
コピペ的関数
無駄なパブリックメンバー
その他lintレベルの自明なフォーマット崩れ
より本質的な可読性(上記を除いたもの)
設計
同じことを2回書かずに済む構造を意識する
一般論として、人間が発生させる失敗について統計を取ると、条件が同じであれば適当な確率に収束します(もしこれに疑いがあれば、自分で統計を取って試してみてください...)
つまり、単純に試行回数が少ない≒同じことを何度も書かない方が、単純な失敗によるバグは少なくなりやすいということです。もちろん、状況によっては敢えて冗長な書き方をしたほうがバグが少ない場合もありえますが、原則としては記述量が少なくなる事を目指す方がよいでしょう。
似たような分岐を何度も書かずに済む構造を意識する(少し難しい)
ある区分に応じた分岐をコードの至るところに埋め込むような実装になると、その区分が増えた場合に、全ての分岐に手を加える必要があり、コードも読みにくくなります。
そこで、区分が増えたことによる分岐がなるべく局所的に済むようなオブジェクト・クラスの設計を心がけます。実際、オブジェクト指向の元になった考え方の一つである、構造化プログラミングでは、以下のような事が提唱されていました。
「データ構造とアルゴリズムは密接に結びついているため、データ構造とアルゴリズムは別々のところで管理するのではなく、一体化したものとして管理をする。これによって、修正の影響範囲を小さく抑えたり、可読性を高めるメリットがある。」
オブジェクトの考え方を持つプログラミング言語では、someObject.method()
のようにしてmethodを呼び出す事ができます。このmethodの実際の定義をsomeObjectのクラスの定義に書いておけば、呼び出し元で分岐する処理を書かずにすみ、誤った処理が混入する可能性を減らすことができます。
変数の寿命は極力短くする、スコープを限定する
単純ですが、ある変数が使用可能な領域を小さくしてしまえば、その変数を間違って使う可能性は減ります。また、この変数の値はどこで変更されて、どの値になっているのか?という事を意識してコードを読む負荷も減り、わかりやすくなります。
関数・変数名を自然な英語に近くする
自然な名前になるようにします。これを徹底すると、自然と関数が単一責務を果たすようになります。
具体的には、名前をつけるとき/関数を修正するときに以下の原則に従うようにします。
関数名は動詞から始める
必要なら目的語をきちんと示す
名前と実態の乖離した嘘をつかない
副作用がある場合や破壊的な場合は、必要に応じてそれがわかるようにする
全体で統一感のある命名方法にする、同じ概念に同じ言葉を使う
ハンガリアンの推奨ではなくて、一つの単語の意味がプログラム全体でなるべく同じ意味になるようにします。あるいは、全てのドメインでの意味の統一が難しい場合には、適当なコンテキストにおいて同じ意味になるようにします。(DDDでいう境界づけられたコンテキスト毎)
これら以外の観点として、変数名の長さとスコープの長さが相関関係になるイメージを持つというのもあります。一文字の変数は特定の短いループや内包表記などに留め、大域的な変数は意味のある名前にする、など。
複雑度を下げる
ガード節などでネストを浅くしてかつ異常時の離脱を促す
インデントが深くなると、単純に今の処理がどの分岐やループに対応しているのかを読むのがつらくなります。
できる限り一つ一つの処理を参照透過にする
つまり、状態によって出力・結果が変わらないようにします。
副作用を極力排除する
つまり、参照透過と合わせて、関数は引数を受け取って戻り値を返すこと以外はなるべくやらないようにします。
(もちろんDBアクセスなどで副作用が必要になる場合はあります)
全体(あるいは一定の領域)で書き方に統一感を持たせる、いわゆる「空気を読む」
名前に限らず、関数やメソッドの書き方などもなるべく統一感をもたせるようにします。
例えば、オブジェクト指向的な記述において、共通的にインターフェイスを定義しておいて、複数のクラスでそのインターフェイスを実装して取り扱うようなやり方があります。これは言語・文法のレベルで統一感を生み、また無駄なコードを削減できます。
関数の内部においても、処理の順序や書き方をできる限り統一するようにします。
(静的な型付けのない言語であっても)型を意識する
関数が参照透過で副作用のないものになると、引数によって戻り値は一意に決まります。この戻り値がどのような型なのか、また受け取る引数の型は何なのか意識すると、コードを読みやすく・書きやすくなります。