以前から読みにくいコードはよくないと思っていたのですが、最近その思いが一層強くなり、またそのへんの話について以前より細かく話せる気がしてきたので、思考の整理も兼ねてここに書いてみようと思いました。
ぼくはWebサービスのバックエンドAPI1を開発する4,5人のチームでリーダーを2年ほど務めていて、チームメンバーの書くコードは基本的に全てレビューするようにしています。
コードの読みやすさ向上はチームで協力して取り組む作業ですから、コードを書き始めるところから、レビューして、マージに至るまでのプロセスにおいて、コードを書く人と、それをレビューする人がそれぞれどのように取り組めばいいか、これまで得られた経験を元に書きました。
なぜ読みやすくするのか
どのように読みやすくするかの前に、そもそもなぜ読みやすくするのかを明確にしておきます。ぼくがコードレビューをするときはそのコードが仕様に完全に準拠できているかどうかよりも、読みやすいかどうかをまず重点的に見ます。それはなぜか。
当たり前ですが仕様に準拠した実装にすることは非常に重要ですね。いや、最も重要と言っていいです。ただし、その仕様はぼくたちが想像しているよりも変わります。
ぼくたちが書くコードは世の中のニーズが変わっていく中で継続的に利益を生み出さなければいけません。そのためには仕様の変更や追加が継続的に発生して、自ずと既存コードを理解したり、それに手を入れたりする必要が出てきます。
一方で、ビジネスはシステムが正しく動くときに最大限の利益を生み出します。正しく動かないときはお金を生むシステムが不完全な状態にあるわけで、最大の利益は生み出せません。つまり、ぼくたちは仕様通り2に動く時間を最大化することでビジネスに最大限の貢献ができるのです。
だから[現時点で完璧に仕様通りであること]は大事なんですが、[今後継続的に仕様通りにしやすいこと]はもっと大事なのです。
そしてこれはぼくの経験則ですが、コードを読みやすくしておくことは、今後継続的に仕様通りにしやすくすることに対してとても効果的です3。これがいま時点での仕様への完璧な準拠をよりも、読みやすいかどうかをより重視する理由です。
読みやすさは主観的か
読みやすさという指標の考え方についても書いておきます。
コードの読みやすさは「ぼくが読みやすい」だけではもちろん不十分で、厳密には、[今後保守する人それぞれが感じる読みやすさの度合いを、各々の保守期間の長さで重み付けしたときの加重平均を最大化する]ことが理想です。でも未来予測はできませんから、人が一般的に混乱しにくくされている度合いをコードの読みやすさと言っていいと思っています。
細かいレベルでのコードのお作法は『プログラミング作法』や『リーダブルコード』などの本で詳細に説明されている内容ですからそちらをご覧いただくのがいいと思いますが、もっと抽象化して表現するなら、ぼくは下記の3点を重視しています。
- 余計な情報がない
- 余計な処理がない
- 命名が真実である
コードを読みやすくする方法
さて、本題です。読みやすさを重視する度合いについては人によって違っても、読みにくい方がいいと思う人はいないと思います。ではなぜコードは読みにくくなるのでしょうか。その原因を明らかにし、それぞれについてぼくが実践する対応策を挙げます。
なぜ読みにくいコードを"書いて"しまうのか
これの原因は大きく2つに分けられると思っています。
「動かすために試行錯誤して動いたら終わりにしたい」
このように考えてコードを書くときは、以下のようなプロセスになりがちです。
- 仕様と設計を理解する。「なるほど、よし実装するぞ。」
- 書き進める。
- 試行錯誤する。「うーん、思ったように動かないな。」
- 試行錯誤する。「これすればいいのかな?んん?なんか動いたっぽいな。」
- 完成。「コードレビューお願いします。」
このときのコードは以下のような特徴があります。
- 処理が遠回りをしている(試行錯誤していてごちゃごちゃした)
- 変数名などが嘘になっている(試行錯誤する中で変数の使いみちが変わった)
- 処理Aと似た処理Bが全然別のやり方を採用している(全体を見渡していないから似た処理だと気づいていない)
これを解決するには完成とする前にいくつかの作業を入れればうまくいきます。
- 試行錯誤する。「これすればいいのかな?んん?なんか動いたっぽいな。」
- 問題を理解する。「なるほど、この処理はこの辺が少し複雑だけどやり方は理解できた。ほかの箇所はシンプルだな。」
- 問題に対して適切な処理の流れを考える。「この少し複雑な部分はこう書いたらいいな。残りは普通にシンプルに書ける。」
- 最初から見直して無駄がないか確認する。「いい感じに書けたかな。これやって、こうして、こうなって、うんうん自然な流れだな。よし。」
- 完成。「コードレビューお願いします。」
このようなステップを踏むことで頭が整理され、機能をより深く理解でき、どの細かなコードの断片についてもそれがなぜ必要で、全体の中でどのような役割を担っているのか説明できるようになります。また、レビューする立場としても読みやすさが上がっていて、読んでわからなかったときでも書いた人に聞けば納得しやすい話が聞けるはずです。
「Don't Repeat Yourself!!DRY!!!」
このように考えてコードを書くときは、以下のようなプロセスになりがちです。
- 仕様と設計を理解する。「なるほど、よし実装するぞ。」
- 書き進める。
- 処理の共通化をする。「あ、こことここ似てるな。共通化しよう。」
- 書き進める。
- 処理の共通化をする。「お、ここも共通化できるな。よしよし。いい感じだ。」
- 書き進める。
- 完成。「コードレビューお願いします。」
このときのコードは以下のような特徴があります。
- 各ユーザシナリオをベースに考えたときに、コードに余計な情報・処理が含まれていて何をしてるのかわからない。
同じコードを記述しないことを気にしすぎるあまり、早すぎる共通化、不適切な共通化をしてしまうことがあります。これを解決するには自信のない共通化はせずに実装を進めるのがいいと思っています。まずはシナリオごとにコードの場所を分けて、それぞれのシナリオに集中したコードを書きます。共通化はもっと問題がクリアになってからでも遅くありません。それまではダサくても[共通化できそうなのにしてない部分]を残した方がうまくいきます。
最初のうちは[似てそうだけど同じじゃない処理]をコピペしてちょこちょこ書き換えて済ませるのはとても良い方法です。そうすればそもそも共通化するのが適切か、共通化すべきならどう共通化するのが適切か、あとでコードを見比べながら検討できるようになります。最初からあれこれ共通化して処理が異なる部分に気づいたら分岐を増やしていくのはうまいやり方ではありません。まずはベタ書き気味にするのです。
これは「あらゆるシナリオに対応できる最強のロジックを書きたい」と思ってコードを書くときにも似たような結果になりやすいです。そのロジックですべての問題を解決するのではなく、誰でも理解できる小さな役割を与えてそれを解決するだけで十分なはずです。
なぜ読みにくいコードを"承認して"しまうのか
「プログラマみんなが熟練者ってわけじゃない。読みにくいコードを書いてしまう人がいるのは仕方がない。でもコードレビューで指摘して修正してもらえば問題ない」。そう思いますよね。わかります。ではなぜ読みにくいコードが今master
ブランチにあるのでしょうか。
これも原因を2つに大別できます。
「バカだと思われたくない」
これは当たり前の気持ちですが、しかしこの思いが強すぎると難解なコード4を読むときに「全然わからない」ということを打ち明けられなくなってしまいます。打ち明けられない場合は頑張って読もうとしますが、時間が無限にあるわけでもないので「わからなかったけど動くならいいや」と思い、諦めて承認してしまうのです。
これを解決するにはバカだと思われる恐怖を克服しないといけないと思います。理解できないことをサラッと聞き出せるようになれば、ソフトウェア開発は簡単になります。
「人間関係を壊したくない」
コードレビューをされる人は自分のコードにあれこれ指摘されると自分自身が否定されていると感じてしまいがちです。「コードが悪いだけで人は悪くないから、自分が責められてると思わないようにしよう!」なんて言う人もいますが、そんな気持ちのコントロールができる人はすごい人だけです。普通の人はつらい気持ちになります。
そしてレビューする人はそれを肌で感じますから、あまり[強く or しつこく]指摘できません。そして数箇所だけ指摘して修正してもらったちぐはぐなままのコードを、もう限界だと察して承認してしまうのです。
これを解決するにはコードレビューする人とされる人の間で[どのようなコードがいいコードか]という点について共通認識のレベルを上げる必要があると思います。なぜ読みやすいことが大切で、その指摘がなぜ読みやすさに貢献するのか、しっかりと省略せずに説明をします。
これは時間のかかることですが、共通認識レベルが低いままでは指摘したときに「よくわからない指摘を繰り返す嫌な人」と思われることを回避できません。
苦境を乗り越える
ここまで読みにくくなる典型パターンについてその原因と対応策を書いてきました。しかし実際にはこれまで書いたこと以外にも思った通りにいかないことがたくさんあります。ここではありがちな状況と、そのときぼくがどのように対応しているかを実体験を元にいくつか挙げてみます。
(コードレビューするときに)コードが読みにくく変更も多くてよくわからない
こういうときぼくは過去に何度も「プルリクを分割してほしい」と言っていましたが、そう言ってわかりやすい形に分割してもらえたことは一度もありません。分割方法についてあれこれ言っても10
の苦労が3 + 3 + 3 + 3
になるような違いしか生まれませんでした。一旦書いた後で言っても手遅れなのです。
共通認識レベルが低いうちはなるべく小さな実装からお願いすることにしています。レビュー時は時間をかけて一緒に読み進めます。
(コードレビューするときに)コードが読みにくく変更も多くてよくわからないし時間もない
まずはこの状況になるまで放置したことを反省します。このとき取れる最善策はそのコードと密結合になっている部分を可能な限り小さくすることだと思ってます。今回は時間がないため全ては理解できないままそのコードをマージしますが、将来それをなんとかするときは密結合になってる単位で対応が必要になります。読みにくさがコンポーネント全体に根を張り巡らせているような場合は全てを書き直さなければいけなくなります。逆に密結合になっている範囲が狭く他のモジュールと適切なインターフェースを介して連携されているのであれば、中身が黒魔術になっていてもあとで丸ごと置き換えることは比較的簡単になります。
よくわからないコードを受け入れるときは、どの範囲で書き直す必要があるのか理解し、覚悟を決めてマージします。これは正真正銘の"技術的負債"を自分の判断で組み込む瞬間です。それを心に刻みます。
(コードレビューするときに)コードが読みにくく変更も多くてよくわからないし時間もないし動きもしない
まずはこの状況になるまで放置したことを猛省します。残り時間やコードの読みにくさの程度にもよりますが、修正するより書き直した方が早そうならそれも考えます。ただ、プログラミングが大して好きじゃない人でもせっかく頑張って書いたコードがなんの役にも立たずに捨てられるのはショックです。書いてくれた人を傷つけないように慎重に言葉を選んで説明しないといけません。
(コードを書くときに)少し難しい処理をもっとスマートに書きたいけどやり方が思いつかない
なぜ読みにくいコードを"書いて"しまうのか
で書いた改善されたプロセスを辿るとこのような状況になることがありますが、レビューを頼める人の中に自分よりプログラミングができると思う人がいるなら、何時間も一人で悩むのは時間がもったいないのでやめます。その場合はそのままプルリクを出してしまい、レビューする人に「ここが微妙だと思ってる」と正直に伝えるといいです。そうすればレビューする人にも考えてもらえるので、そのチームが書ける最高のコードに近づきやすくなります。
ここまで、読みにくいコードが生まれる原因とその対応策を書いてきました。
スマートにやりたいという思いに反して、読みやすいコードを作ることは泥臭く強い心を必要とする作業です。そんな作業でも一つ一つ正直に誠実に取り組んでいれば、きっといい結果につながると思います。
これはぼくが携わるWebサービスのバックエンドAPIの開発者の一人としての視点だけで書いたものであり、どの程度他の人の役に立つのかはわかりませんが、参考になる部分があれば幸いです。