[プログラミング]よいコードを書くために
コードをたくさん読んでいると、よくできていて参考にしたくなるコードや、身の毛もよだつクソコードなど、色んなコードに出会う。
自分一人で書いていた頃は、コードの良し悪しは全て自分にはね返ってきていたのだが、チームを組むとそうはいかない。
人の書いたコードで苦しむこともあれば、自分の書いたコードで人を苦しめることもある。
そんなことにならないよう、少しでも良いコードを書くために意識するべきことをまとめてみた。
読むのに苦労しないコードを書く
書かれたコードは、それが使われ続ける限り、何度も読まれる。
読む人は自分かもしれないし、他のチームメンバーかもしれない。別の会社の顔も知らない人かもしれない。
そんな人たちでもスラスラ読み解け、理解できるコードは、きっと良いコードだ。
冗長さを制御する
大体の悪いコードは長い。どんなに素晴らしい設計がされていても、長いと読む気力が失せる。コードは短いに越したことはない。
ただ、短くすることだけを目的にしてしまうと逆に読めないコードになる。
Pythonならリスト/辞書内包、lambda、map/reduceが乱用されたコードを考えてみるとわかりやすい。
このような場合は多少行数が増えるとしても、forや普通の関数定義を使った平凡なコードの方が良いコードになることは多い。
どちらの方が読みやすいかを常に吟味しながら、冗長に書くか短く書くかを選択していく必要がある。
問題を分割する
長い、読めないコードができてしまう原因の一つとして、問題を分割できていないというのがある。
やりたいことに対して、それに必要な処理をずらーっと全部ひとところに書いてしまうようなのだ。
例えば「ユーザーを登録したい」というシンプルな目的に対しても、それを実現するために幾つもの細かい問題を解決しているはずだ。
リクエストメソッドを判別する、POST値からユーザー名とメールアドレスを取り出す、値を検証する、ユーザーオブジェクトを作ってsaveするetc
これら細かな問題を、関数やクラスの形に分割できないかを検討してやらなくてはいけない。
問題を分離しておけば、細かく処理を追うか、おおまかに流れを掴むかを選択でき、読み手の負担はぐっと下がる。
共通化やテスタビリティ向上の面でも、問題を正しく捉えて分割するというのはとても重要だ。
分離したら再設計する
長い処理を分離すると、分離した先は大抵なにかデータを入力し、その結果として出力値を返すようなものになる。
間違った分離をしてしまうと、例えば値の検証の関数が、リクエストオブジェクトを取ってフォームオブジェクトを返す みたいなわけのわからない動きをするものになってしまうことがある。
こういうものは大抵、ベタっと書かれていたコードの一部分をカットペーストで移動して、移動前に使っていたコンテキストを引数にしただけのもので、あまり価値がない。単純に処理のジャンプ回数が増えただけだ。
値の検証であれば、未検証の値の辞書を渡して、検証済みの値の辞書を返すような、入力と出力の関係がシンプルなものの方が使いやすいだろう。
入出力のみに限らず、コードを分離するのであれば、その部分での関心ごとにちゃんと注目し、かくあれかしという設計に焼き直してやることが必要だ。
適切な名前
関数や変数の名前というのは、コードを読み解くにあたって大きな手がかりになる。
それはつまり、名前がおかしければそれだけ読み解く障害になるということでもある。
ユーザーオブジェクトが入っている変数名が"a"とかだったら、もうなんのことやらさっぱりという感じだろうし、グリッドの行と列に順次アクセスするためのループ変数がiとjだったら、どっちが行でどっちが列なのかぱっとは分からない。
また、関心ごとの分割においても、分割した関心ごととその動作を正しく表す名前を関数につけてやる必要がある。
create_userという関数でユーザーを作り…つつ登録完了メールも飛ばす、なんてことをしているとしても、使い手からは全く想像できない。
プログラムとしては名前なんてただの識別子でしか無いが、読み手にとってはそうじゃない。
嘘の説明を書くなんてことは、しないようにしよう。
型にはめる
ある程度型に沿ったコードというのは、それだけで多少読みやすくなる。
場所によってタブインデントだったりスペース4つだったり、ブロックの中括弧がifの行についてたり、次の行についてたりするのは地味にストレスになる。
PEP8でも社内コーディング規約でもいいが、なにか指標があるのであれば、できるだけそれに従って書くのが良いだろう。
規約自体がそもそも良くない場合はどうするかとかは、また別の話。
コメントを必ず書く
ここまで、どのようにコードを書けば読みやすくなるかという話をしてきた。
しかし、どんなに綺麗に設計しようが、素晴らしい名前付けをしようが、意味を伝えるということにおいては自然言語で書かれたコメントには敵わない。
どこまでいってもコードだけで表せるのは「この関数をよびだす」だとか「あの変数に代入する」だとか、その程度の意味までだ。コードは決して語らない。
各処理の要点や、関数の概要は、必ずコメントとして残し、処理内容と一致するように常にメンテしていく必要がある。
コメントを書かないという選択肢はない。
テストをかく
ユニットテストを書いてみると、自分のコードがよいコードかどうかが結構な精度で分かる。
問題を適切に分割できていれば、要は設計がうまくいっていれば、必然にテスタビリティも上がっていくので、ユニットテストが簡単に書けるようになっているはず。
逆にユニットテストレベルで苦労するようであれば、なにかしらよいコードの条件を破っていると思って間違いない。
また、書いたテストを今後も運用し続けていくかはともかく、「書こうとしてみる」ことの方が重要だ。
なので、最悪まともに書けなくてテストコードを捨ててしまうことになっても構わない。
次に書くコードでは少しでもテストを書きやすいように意識してみると、きっと何かしらの改善があるはずだ。
疑問をもつ
自分が書いたコードが本当に悪いコードじゃないかという疑問を常に持ち続ける。
悪いコードを書いていないか、たまに振り返ってみる。人に評価を求めてみる。
書いてしまっているなら、何が悪いのか、どこを変えればよくなりそうなのかを考えてみる。相談してみる。やってみる。
これがないと一生改善は始まらない。
おわりに
誰もが悪意を持ってクソコードを書こうとしている訳ではないが、経験が足りなかったり、時間が足りなかったりして、悪いコードを生み出してしまうことがある。
そんな人に指導するとき、もしくは、自分が悪いコードを生み出さないようにするために、この記事が助けになれば嬉しい。