こんなコードだとわかりやすい
僕が考える良いコードの特徴(条件)を挙げてみると、
- ぱっと見たら、だいたい何をやっているのかがわかるメソッド名
- ぱっと見たら、だいたい中身が何なのか想像がつく変数名
- ぱっと見たら、だいたい何をやっているのかが把握できるメソッドの内の処理フロー
- 驚きが少ないメソッド
- 副作用が少ないメソッド(責務が1つしかないメソッド)
- DRY原則を守っているコード
だいたいこんな感じ。
つまり「すんなり読めて、すんなりわかるコード」が理想。
プログラムが小さいうちや、一人で開発しているうちは「汚くてわかりにくいコード」であっても「自分さえわかればOK」で済んじゃうけど、プログラムの規模が大きくなったり、複数人で開発するようになると、「汚くてわかりにくいコード」は絶望的に開発効率を下げる。
こんなコードはわかりにくい
たとえば上の反対で、
- メソッド名だけ見ても何をやっているのか想像が付かないメソッド
- 変数名だけ見ても中身が何なのか想像が付かない変数
- 複雑怪奇で巨大迷路みたいなメソッド実装
- 「まさかこんなところでこんなことを!?」と思うようなサプライズがふんだんに盛り込まれたメソッド
- メソッド名に反して「あれもこれもそれも」全部やろうとするとメソッド
- あちこちにほぼ同じコードが埋め込まれていて、「まだ他にもあるんじゃないか」と疑心暗鬼になってしまうコード
みたいなコードだと、隅から隅までコードを読まないと仕様や挙動が理解できない。
小さなプログラムならまだしも、大きなプログラムになると「隅から隅まで読んで理解する」というのはあまりにも時間を浪費してしまう。
なので、「すんなり読めて、すんなりわかるコード」になっていることが大事。
業務ではコードを書く時間よりも読む時間の方が多い、だから……
「すんなり読めて、すんなりわかるコード」なら多少規模が大きくても、「だいたいこういうふうに作ってるのね〜」「今回の機能追加はこのへんを触ればOKね〜」というのが比較的すぐわかる。
もちろん、ある程度の規模になってくるといくらがんばっても「すんなり」では済まない場合も増えてくるけど、それでも最初に挙げた特徴を兼ね備えたコードとそうでないコードでは、開発効率に雲泥の差が出てくる。
業務ではコードを書く時間よりも読む時間の方が多いので、読む時間を節約できるコードになっていることの方が重要。
「すんなりわかるコード」を書くにはいろんな配慮が必要になるので時間がかかるけど(注:ここではリファクタリングも書く時間に含みます)、将来の「読む」時間を節約できるなら、「書く」ためにかけた時間はペイできるはず。
まとめ
20年もプログラマをやっていると、コードレビューしたときにこれまでの経験から「むむ、この実装だと将来痛い目を見るぞ?」という嗅覚が働くようになってきました。
で、その嗅覚をある程度言語化すると最初に挙げたような条件になるのかな〜と思って、このエントリを書いてみました。
おまけ:良いコードの書き方を学ぶのに役立つ参考文献
「良いコードと悪いコードって具体的にどんなのよ!?」という具体例を知りたい場合は以下のリンクを参考にしてみてください。
書籍で良いコードの書き方を学ぶ
定番中の定番、リーダブルコード。
ただし、僕はあまりちゃんとリーダブルコードは読んでなくて、CODE COMPLETEっていう本をがっつり読み込みました。
あと、まだ最後まで読み切ってないのですが、最近買った「プログラマー脳」という本もどういう理屈で人間の脳はコードを理解できるのか(もしくは理解しにくくなるのか)ということを解説してあってなかなか面白かったです。
リファクタリングやDRY原則の原典に触れる
今となっては「リファクタリング」や「DRY」といった用語は、みんな当たり前のように知っていますが、あらためて「リファクタリングとはなんぞや」「DRYとはなんぞや」というのを原典で学び直すのも良いかもしれません。
抽象化の概念を理解する
ぱっと見たら、だいたいわかるメソッド名や変数名を付けるためには、処理やデータをほどよく抽象化して考えるスキルが必要になります。フィヨルドブートキャンプで生徒さんを教えていると、ときどきこの「抽象化」が苦手な生徒さんを見かけるので、そういう場合は「そもそも抽象って何?」というのを以下の本で学んでもらっています。
「名前重要」を肝に銘じる
Rubyのパパ、Matzの座右の銘は「名前重要」です。名前重要、ほんまそれ。
xn--97-273ae6a4irb6e2hsoiozc2g4b8082p.com
でも「いい名前」を考えるのはとっても大変😣
プログラミングで一番長い時間悩むのが「いい名前を考える時間」かもしれない。
— Junichi Ito (伊藤淳一) (@jnchito) 2020年7月9日
ロジックを考えたりコードを書いたりする時間はスキルの向上とともにどんどん速くなってくるけど、適切な名前(クラス名、メソッド名、変数名etc)を考えるスピードはコードを書くスピードほど速くならないんだよねえ。
品詞に気を付ける
動詞や名詞の使い分けがぐちゃぐちゃだと、「メソッドだと思ったのに変数だった」とか「変数だと思ったらメソッドだった」みたいな誤解を生じさせやすいです。(()
が省略できるRubyなんかは特に)
コメントはいつでも書けばいい、というものではない
コメントはたくさん書いた方がわかりやすくなるはず、と思うかもしれませんが、必ずしもそうではありません。
コメントを書くことのデメリットも把握しておきましょう。
配列の添え字に意味を持たせない
配列の添え字に意味を持たせてしまうと、一気にわかりにくくて、もろいプログラムになります。
qiita.com
配列の二人三脚をしない
「この2つの配列には過不足なく同じ件数のデータが入ってるはず」という希望的観測に基づいたコードを書くのは避けましょう。
qiita.com
メソッドは呼ばれる側を後ろに定義した方が読みやすい、かも
好みがわかれるかもしれませんが、個人的にはメソッドは呼ぶ側を手前に、呼ばれる側を後ろに定義した方が読みやすいと感じています。
マトリョーシカメソッドを避ける
コールスタックを不必要に深くしたようなメソッド設計だと、処理の概要が把握しづらくなります。
blog.jnito.com
無味無臭な名前を避ける
過度に抽象化すると無味無臭なメソッド名や変数名が生まれます。あくまで「ほどよい抽象化」が大事です。
プログラムを書くとき、こんな変数名を付けるのはNGです🙅♂️
— Junichi Ito (伊藤淳一) (@jnchito) 2022年9月17日
data
info
item
element
obj
array (ary, arr)
list
こういう変数名を僕は「無味無臭な名前」と呼んでます。プログラムに出てくる変数は全て何らかのデータなので、dataなんて名前を付けてないの同じ。中身が想像できる名前を付けましょう!
コンピュータが理解できるコードと人間が理解できるコードは別
コンピュータ(コンパイラ)を満足させたから自分も満足、ではいけません。
do_something(do_hoge.xyz(do_piyo.do_fuga{|x| x .foo(bar.baz) || []}))
— Junichi Ito (伊藤淳一) (@jnchito) 2020年11月11日
みたいなコードはコンピュータが理解できても、人間はぱっと理解できない。
なので「書いたコードが動いた=文法的に問題がないからOK」じゃなくて、「自分以外の誰かが見てもぱっと理解できるか?」も常に自問しましょう。
テストコードはあえてDRYを捨てる
DRY原則は大事ですが、テストコードではほどほどにしないと読みづらいテストコードができあがります。
その他
- 変数の寿命は極力短くする
person = find_user
のように左辺と右辺で微妙に違う名前を付けない- 変数はなるべくイミュータブルに扱う(破壊的変更をなるべく避ける)
- 変数(または引数)の再代入を避ける
- メソッドの途中でreturnするときは、なるべくガード節(早期リターン)に限定する(ループ処理におけるnextやbreakも同様)
- シャドーイングを避ける(既存のメソッド名と同じ名前のローカル変数を作らない)
check_xxx
というメソッド名を避ける(checkだと何をどうチェックするのかわからない)
あわせて読みたい
このエントリの続編として、きれいなコードを書く「だけ」ではすんなり読み解くことができない大きなコードベースの場合、どういった方法で書き手の意図やコードの背景を残すことができるのか、というお話を書いてみました。