give IT a try

プログラミング、リモートワーク、田舎暮らし、音楽、etc.

シンプルでわかりやすいコードを書くためにあなたがすべきこと

はじめに

先日、とある知りあいのRubyプログラマからこんな相談を受けました。(内容はちょっとボカしてます)

社内のコードレビューでもっときれいなコードを書けるようになった方がいい、と言われました。

「きれいなコードを書けるようになれ」と言われても、具体的にどうすればいいかわかりません。

伊藤さんのアドバイスを聞きたいです。

この内容だけだとどんな問題があるのかわからないので、実際に指摘を受けたRailsアプリのコードを見せてもらいましたが、確かに「もうちょっと頑張りましょう」と思うような点がチラホラありました。

ただ、具体的にどうすればいいの、という答えは一言では言えません。
というわけで、今回のエントリではこの悩みを解決するのに参考になりそうな話をあれこれ書いてみようと思います。

ちょっと「臭う」コードの例

まず、みなさんと「いいコード」「悪いコード」のイメージを共有するために、その人が書いたコードを紹介します。

その人に見せてもらったコードの中で気になったのは、一つ一つのメソッドがやや長く、内部の条件分岐も多かったところです。
その中でも最も複雑なメソッドは50行ぐらいの長さで、こんな条件分岐を持っていました。

def SomeController < ApplicationController
  # ...

  def create
    if a
      # ...
    end

    # ...
    foo = b ? bar : baz
    # ...

    if c
      # ...
    elsif d
      if e
        # ...
      else
        if f
          # ...
        else
          # ...
          redirect_to xxx and return
        end
      end
    else
      # ...
    end

    if g
      # ...
      if h
        # ...
      end

      if i
        if j
          # ...
          respond_with xxx
        end
        # ...
      else
        # ...
        redirect_to xxx and return
      end
    else
      # ...
      respond_with xxx
    end
  end

  # ...
end

うーん、確かにこれは指摘を受けても仕方がない感じがしますね。。。
いわゆる「コードの臭い」をプンプン感じます。

「コードの臭い」とは以下のような兆候のことです。

コードの臭い(英: Code smell)とは、コンピュータプログラミングにおいてプログラムのソースコードに深刻な問題が存在することを示す何らかの兆候のことを言う。


コードの臭いが示す深刻な問題は、小さく管理された手順でリファクタリングする短いフィードバックサイクルを廻し、それ以上のリファクタリングが必要なことを示すコードの臭いがないかどうか、設計を検査しなければならない。

コードの臭い - Wikipedia
メソッドの複雑度を意識する

自分でいうのも何ですが、僕だったらよっぽどのことがないとこんなメソッドは書かないです。
なぜかというと「複雑度が高すぎて、全分岐を網羅するようにテストするのが大変になるから」です。

みなさんはメソッドの複雑度(循環的複雑度、サイクロマチック複雑度)ってご存知でしょうか?

循環的複雑度(英: Cyclomatic complexity)とは、ソフトウェア測定法の一種である。Thomas McCabe が開発したもので、プログラムの複雑度を測るのに使われる。プログラムのソースコードから、線形的に独立した経路の数を直接数える。

循環的複雑度 - Wikipedia

Wikipediaにはこんなサンプルコードが載っています。

if (c1) {
    f1();
} else {
    f2();
}
if (c2) {
    f3();
} else {
    f4();
}

詳しい説明はWikipediaを読んでもらうとして、上のコードは条件分岐が2つあるので複雑度は「2 + 1 = 3」となります。(分岐やループが全くない場合の複雑度は1なので、1を足します)
また、「完全な経路網羅率を達成するには、4個のテストケースが必要」になります。

再びWikipediaを引用します。(強調は筆者)

一般に、あるモジュールを完全にテストしようとした場合、全ての実行経路を通す必要がある。これの意味するところは、循環的複雑度の大きいモジュールの方が経路数が多く、従ってテストケースも多く必要になるということである。また、複雑度の大きいモジュールは、ソースコードの意味を理解するのに多くの経路を追わなければならず、読解がより困難になる。

循環的複雑度 - Wikipedia

まあ、「複雑度が大きいほどテストケースも多く必要になる」「読解も困難になる」というのは、プログラマの方であれば自然に理解できると思います。

実際に複雑度を測ってみる

RubyにはSaikuroという複雑度測定ツールがあります。
使い方はこちらのQiita記事に載っています。

このツールを使って、先ほどのコードの複雑度を測定してみました。
以下はその結果です。

f:id:JunichiIto:20160518071832p:plain

ご覧のとおり、createメソッドの複雑度は11と出ています。
さらにセルの背景色が赤色になっています。

こちらの記事によると、複雑度とバグの混入確率には以下のような関係があるそうです。

循環的複雑度 複雑さの状態 バグ混入確率
10以下 非常に良い構造 25%
30以上 構造的なリスクあり 40%
50以上 テスト不可能 70%
75以上 いかなる変更も誤修正を生む 98%

先ほどのコードは複雑度が11だったので「構造的なリスクあり」で、バグ混入確率が40%に該当します。
背景色が赤色になっていたのは、おそらくそのせいでしょう。

10以下が「非常に良い」となっていますが、個人的な感覚では5以下ぐらいが「非常に良い」かなと思います。
6~10だと「許容範囲だが、避けられるならなるべく避けたい」という感じです。

(訂正)英語版のWikipediaの記事を読むと、複雑度とバグの相関性はハッキリしていないそうです。なので上の表は話半分で眺める程度にしておくのがいいと思います。

また、メソッドの長さも理想は10行以下、長くても30行ぐらいに抑えたいところです。

シンプルでわかりやすいコードを書くために

さて、コードの複雑さを定量化する方法はわかりましたが、複雑ではないコード、いや、シンプルでわかりやすいコードを書くためにはどうしたらいいでしょうか?
僕は先ほどのコードの仕様を完全に理解していないので、「ここをこう直せば良い」という具体的な改善案は出せませんが、代わりに一般論としてどういう知識や訓練が必要になるのかを考えてみます。

技術書を読んでパターンを知る、手数を増やす

コードの書き方のパターンや問題に対処する手数がたくさんあればあるほど、「こういうコードは良いコード」「こういうコードは悪いコード」「こういう場合に有効な手法はこれ」というふうに、遭遇した問題に応じて有効な手段(と避けるべき手段)を選べます。
そうした知識は技術書の中に形式知化されているので、良い技術書を読めば「良いテクニック」をしっかり学ぶことができます。

以下に僕が実際に読んで良かったと思う本を挙げていきます。

CODE COMPLETE

このブログで何度も登場しているので「またか」と思われるかもしれませんが、まだ読んでいない人がいたら「とりあえず読め」と言いたくなる本です。
プログラマ歴2~3年目ぐらいの人に特にオススメです。
「良いコード」と「悪いコード」の例を懇切丁寧に教えてくれます。
値段は高いけど、ちゃんと読めば十分元は取れますよ!

CODE COMPLETE 第2版 上 完全なプログラミングを目指して

CODE COMPLETE 第2版 上 完全なプログラミングを目指して

CODE COMPLETE 第2版 下 完全なプログラミングを目指して

CODE COMPLETE 第2版 下 完全なプログラミングを目指して

リーダブルコード

「CODE COMPLETEはどうしてもハードルが高い」という人にオススメなのがこちらです。
個人的には「ダイジェスト版のCODE COMPLETE」という印象を受けました。

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

  • 作者: Dustin Boswell,Trevor Foucher,須藤功平,角征典
  • 出版社/メーカー: オライリージャパン
  • 発売日: 2012/06/23
  • メディア: 単行本(ソフトカバー)
  • 購入: 68人 クリック: 1,802回
  • この商品を含むブログ (140件) を見る

リファクタリング

プログラミングをやってる人なら「リファクタリング」という用語を耳にしたことがあると思います。
その用語が広まるきっかけになったのがこの本です。
先ほど出てきた「コードの臭い」という言葉もこの本から広まりました。

単に用語を広めただけでなく、具体的かつ実践的に「イケてないコードとその修正方法」を説明してくれます。
ずいぶん昔(1999年)に発行された本ですが、この本に書かれている考え方は今でも有効なものがたくさんあります。

新装版 リファクタリング―既存のコードを安全に改善する― (OBJECT TECHNOLOGY SERIES)

新装版 リファクタリング―既存のコードを安全に改善する― (OBJECT TECHNOLOGY SERIES)

デザインパターン

RailsのようなWebフレームワークを使って開発していると、デザインパターンを知らないと全くコードが書けないとか、自分でデザインパターンに沿ったコードを書いたりしないといけないとか、そういう機会は滅多になりません。
ですが、オブジェクト指向言語を使うのであれば、デザインパターンに関する知識は「ある種の教養」として知っておいた方がいいです。

デザインパターンそのものを直接使うことはなくても、デザインパターンを勉強すればオブジェクト指向設計やオブジェクト指向プログラミングに関する有効な考え方を理解できます。
また、それほど機会が多くないとはいえ、デザインパターンを知っていると新しいライブラリやフレームワークの使い方や設計思想を速く理解できたり、他のプログラマと「これはxxxパターンで作ってあるから」と意思疎通を図りやすくなったりします。

というわけで、やはりデザインパターンは一度は勉強しておくべきだと思います。

増補改訂版Java言語で学ぶデザインパターン入門

増補改訂版Java言語で学ぶデザインパターン入門

テーブルの正規化に関する本と、SQLアンチパターン

リレーショナルデータベース(RDBMS)を使うプログラムであれば、データベース設計、特にテーブルの正規化に関する知識は絶対に持っておくべきです。
テーブルの設計を間違えると、どうあがいても複雑で分かりにくいコードしか書けない状況に陥ってしまう場合があります。
いつも「なんとなく」でテーブル定義をしているプログラマは、一刻も早くテーブル設計の技法を学びましょう!!

僕はその昔「ビジネス環境の変化に強いデータベース設計」という本を読んで勉強しましたが、データベース設計に関する本はたくさんあるので、必ずしもこれにする必要はありません。
書店で立ち読みして「これがわかりやすそう」と思った一冊を選んでください。

また「SQLアンチパターン」はその名に反して(?)、SQLの書き方よりもデータベース設計に関する話が多かったです。
他の人(未来の自分を含む)に迷惑を書けないよう、反面教師としてアンチパターン(ダメなパターン)を学びましょう。

ビジネス環境の変化に強いデータベース設計―仕様変更なんて恐くない!

ビジネス環境の変化に強いデータベース設計―仕様変更なんて恐くない!

SQLアンチパターン

SQLアンチパターン

使用する言語やフレームワークに特化した知識を仕入れる

ここまで紹介した技術書は言語やフレームワークを問わず、幅広く使える内容の本が中心でした。
しかし、シンプルでわかりやすいコードを書くためには、言語やフレームワークに特化した知識も持っておいた方が良いです。
言語やフレームワークの本といっても、「はじめてのxxx」のような初心者向けの本ではなく、中級者向けのちょっと硬派な本を選んだ方が自分のためになります。

僕はRubyやRuby on Railsをメインで使っているので、Ruby関連ではたとえば以下のような本がオススメです。

プログラミング言語Ruby

Rubyの文法や、言語仕様が非常に詳しく載っている本です。
Rubyのコードを書いたり、動かしたりして「なんでこうなるの??」と疑問に思ったときは、この本に答えが載っていることが多いです。
対象となるバージョンは1.8や1.9なのでちょっと古いですが、大半の内容は今でも有効なのでRubyプログラマなら一冊持っておくことをオススメします。

プログラミング言語 Ruby

プログラミング言語 Ruby

  • 作者: まつもとゆきひろ,David Flanagan,卜部昌平(監訳),長尾高弘
  • 出版社/メーカー: オライリージャパン
  • 発売日: 2009/01/26
  • メディア: 大型本
  • 購入: 21人 クリック: 356回
  • この商品を含むブログ (129件) を見る

Effective Ruby

Rubyに関する「こういうケースではこういうコードを書こう」「こんなコードを書くと思わぬバグを生むよ」といった役立つ知識を解説してくれている本です。
これも知っているか、いないかで自分の書くコードが大きく変わってくるはずです。

Effective Ruby

Effective Ruby

Rails3レシピブック

もうすぐRails 5が出そうなので、ここに載せるのはちょっと迷いましたが、個人的には非常に役に立ったので載せておきます。
Railsを始めて間もない頃、この本には逆引き形式で「おお、こんなときはこのメソッドを使えば、めっちゃ便利やん!」という知識をたくさん教えてもらいました。
情報が古くなりつつあるので、今これを買えとは言いません。
そうではなく、僕が言いたいのはフレームワークの知識をしっかり頭に入れておけば、シンプルでわかりやすいコードを書くのに役立ちますよ、ということです。

Rails3レシピブック 190の技

Rails3レシピブック 190の技

Rubyスタイルガイド(ネット記事)

いわゆるRubyのコーディング規約です。
公式な規約はないので(たぶん)、ネットを調べるといくつかコーディング規約が見つかりますが、Rubyスタイルガイドは比較的メジャーなコーディング規約のようです。

RubyやRailsでリファクタリングに使えそうなイディオムとか便利メソッドとか(ネット記事)

僕がその昔に書いたQiita記事です。(手前味噌ですいません)
Rubyを始めて間もない人が陥りがちな「遠回りした書き方」と、それに対する処方箋を書いています。

ソフトウェアデザイン2016年4月号にもほぼ同じテーマで記事を書いています。
新しく書き下ろした内容も多いので、こちらもチェックしてみてください。

ソフトウェアデザイン 2016年 04 月号 [雑誌]

ソフトウェアデザイン 2016年 04 月号 [雑誌]

その他、コードを書く際に役立つ考え方

シンプルでわかりやすいコードを書くためには、技術書を読むことだけがすべてではありません。
他にもこんな対処法や考え方があります。

テストコードを書いて、徹底的にリファクタリングする

テストコードを先に書くといわゆる「TDD(テスト駆動開発)」になりますが、TDDにこだわらなくてもOKです。
実装コードを書いた後にテストコードを書くという「実装ファースト」な開発の仕方も僕はよくやります。

要は、

  1. テストコードを書いて雑な実装コードを書く(または雑な実装コードを書いてテストコードを書く)
  2. 雑な実装コードを徹底的にリファクタリングする

というステップを踏めば良い、ということです。

雑なコードをそのまま放置すると、コードレビューのときに厳しく指摘されます。
なのでリファクタリングが必要ですが、テストコードがないとコードを直した後の動作確認に時間がかかったり、思わぬバグを埋め込んだりしてしまいます。
また、最初から完璧なコードを書くというのは難しいですし、それを目指すと必要以上に時間がかかったりします。

というわけで僕は前述したようなステップで実装するのが効率面と品質面で一番良いと考えています。

実装コードとテストコードをちょっとずつ太らせていく

「テストコードを書いて、徹底的にリファクタリングする」と似ていますが、こちらはちょっと視点が異なる考え方です。

コードを書く前に「これをプログラミングするのは複雑でややこしそうだ」と感じたら、一気に全部実装せずにちょっとずつ実装していきます。
それだけでなく、ちょっと実装したらそれにあわせてテストコードも一緒に書きます。

ちょっと実装する/テストコードを書く → ちょっと実装する/テストコードを書く → ちょっと実装する/テストコードを書く → ちょっと...

・・・と繰り返していくと、最後にはややこしい実装がテストコード付きで完成します。
実装する順番は、最初に正常系のフローを実装して、後半で異常系のフロー(エラー処理等)を実装する、というふうに工夫するのも良いでしょう。

ちゃんとテストコードが書けていれば自信を持ってリファクタリングできます。
メソッドを分割したり、重複を無くしたりするなどして、コードをどんどんキレイにしていきましょう。

自分の中のハードルを下げない

自分の中のハードルを下げない(自分よりレベルが低い人を見て安心しない)、というのも大事な考え方だと思います。

たとえば、冒頭で示した複雑度11のコードは、僕がこれまで見てきたコードの中ではそこまでひどいわけではありません。
前職や前々職では1メソッドが数百行あって、無数のループやら分岐やらローカル変数やらグローバル変数やらが入り乱れる最悪なコードをたくさん目撃してきました。
それに比べればずいぶんマシな方です・・・が!!!

自分よりレベルの低い人を見て安心すると成長がそこで止まってしまいます。
そうではなく、シンプルでわかりやすいコードを追求するためには常に上を見続けなければいけません。

ツールを使ってコードの品質を測定する

「いいコード」「悪いコード」は感覚的なところも大きいですが、記事の前半で紹介した循環的複雑度のように、定量化することで善し悪しをある程度判断できる場合があります。
自分では自分のコードの善し悪しをちゃんと判断できているか自信がない、チームの中でいいコードと悪いコードの判断基準が違って言い争いになる、といった場合はツールで機械的に判定してみるのもいいかもしれません。

たとえばRubyであればmetric_fuというツールがあります。(先ほど出てきたSaikuroもこの中に含まれています)

コーディング規約をチェックするRubocopというツールもあります。

テストコードの網羅率を調べる場合はSimplecovというツールが使えます。


また、その昔C#を書いていた頃はこんなツールを使ったりしていました。


ツールの数値にがんじがらめにされるとそれはそれで開発がしにくくなってしまいますが(「xxxはxx以下でないとリリースさせん!」みたいなルールができるとか)、開発の補助ツールとして測定された数値を参考にするのはよいと思います。

そもそもの仕様や設計を見直す

場合によっては、コードをどう書くか、という小手先の話ではなく、「仕様を見直すべき」「プログラムやデータベースの設計を見直すべき」という根本的な解決策が必要になるケースもあります。
ユーザーの目的を確認した上で実装が複雑にならないような仕様を選ぶ、既存のテーブルをきちんと正規化する、というふうに、「コード以前」の改善策を検討してみましょう。

チーム内でコードレビューしあう

チーム内でコードレビューをしあって、他の人のコードを読んだり、自分のコードを読んでもらったりするのも非常に有効です。
技術書やツールと違って、その人と意見交換することができます。
また、コードの善し悪しを議論するだけでなく、コードレビューしてもらうことで致命的なバグやセキュリティ問題が発見される場合もあります。

コードレビューではGitHubのPull requestを使うと非常に便利です。
まだ実践していない開発チームはぜひコードレビューのプロセスを取り込んでください。

やむを得ないケースがあることも理解する(でもテストコードは書く!)

これは最後の手段ですが、様々な事情で「本当にどうしようもない」ケースはたまにあります。
冒頭で紹介した複雑なコードも、いろいろ話を聞いていけば「ああ、それなら仕方ないね」という結論になるかもしれません。

しかし、そのような場合でも「泣く泣くこの実装にせざるを得なかった」という自覚があるのと、自覚がないのでは大違いです。
自覚がない人はきっと、今後も長くて複雑なコードを量産することでしょう。

ちゃんと自覚を持っているあなたは「なぜこうなったのか」「なぜこうせざるを得なかったのか」をコメントとして懺悔して説明しておきましょう。

また、実装が複雑になってしまった場合でもせめてテストコードはしっかり書いておいてください。
テストコードがない複雑な実装コードは、のちのち誰も触れない「負の遺産」になってしまいます。

Rubyであれば前述のSimplecovを使えばテストコードの網羅率を確認できます。
複雑な条件分岐があるメソッドでも、ちゃんと全部の行が実行されているかどうか確認してください。

まとめ

というわけでこのエントリでは「シンプルでわかりやすいコードを書くためにあなたがすべきこと」と題して、いろいろな考え方や手法を紹介してみました。
自分ではこういったことはもはや当たり前のようにやっているので、改めて言語化するのは難しかったですが、思いついたことをあれこれと書いてみました。
このエントリが「もっといいコードが書けるようになりたい!」と考えているみなさんのお役に立てば幸いです。

あわせて読みたい

技術書に関するエントリはこのブログでもよく登場しています。
もっといろんな技術書を知りたい、という人は以下のエントリを読んでみてください。


本文でテストコード、テストコード、と何度も口うるさく書きましたが、そもそも「テストコードって自分ではちゃんと書けない・・・」と思っている人も意外と多いと思います。
もしあなたがRailsプログラマなのであれば、電子書籍「Everyday Rails - RSpecによるRailsテスト入門」がオススメです。
こちらの本ではRSpecを全く知らない人でも理解できるように、テストコードの書き方を説明しています。

2016年5月31日までチャリティセールをやっているので、テストコードを書けるようになりたいと考えている人は、この機会にぜひどうぞ!