循環的複雑度を活かしたバグ潜在リスクの軽減

昨日のエントリーに引き続き、このエントリーは「Software Test & Quality Advent Calendar 2011」における12/19分として書いています。

今日は、少しばかりアカデミックな話。
でも、うまく活用すると、品質改善のための強い武器になることでしょう!

循環的複雑度とは?

まずは、今回のタイトルにも書いている「循環的複雑度(Cyclomatic Complexity)」というメトリクスの説明から。

循環的複雑度は、Thomas McCabe 氏が開発したものであり、簡単に言うと、コードの複雑性を数値化したものです。

ソースコードの一部の循環的複雑度は、ソースコード内の線形独立な経路の数である。実際、if文やfor文のような分岐点のないソースコードの場合、その複雑度は 1 であり、そのコードには1つの経路しかない。コードに1つのif文が含まれていれば、コードには2つの経路があることになる。つまり、一方はif文での条件が真となる場合の経路で、もう一方はそれが偽となる場合の経路である。
(中略)
一般に、あるモジュールを完全にテストしようとした場合、全ての実行経路を通す必要がある。これの意味するところは、循環的複雑度の大きいモジュールの方が経路数が多く、従ってテストケースも多く必要になるということである。また、複雑度の大きいモジュールは、ソースコードの意味を理解するのに多くの経路を追わなければならず、読解がより困難になる。

循環的複雑度 - Wikipedia

まあ、細かい話は置いておいて、以下では、循環的複雑度が何の役に立つかを説明しましょう。
(アカデミックな話と言いつつ、複雑度の計算式などの話は飛ばします...)

「循環的複雑度」が何の役に立つのか?

1. バグ混入の確率を減らす

循環的複雑度の値に応じて、そのプログラムのバグ混入率は、以下のようになるそうです。

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

実際にコードを書いてみると、「10以下」を徹底するのは難しいのですが、きれいなコードを書いていけば、「30以下」は十分に徹底できるレベルです。

上記のような指標値を基にリファクタなどをしていくと、バグの混入自体を軽減することができます

2. xUnitのテストケース数の指標となる

循環的複雑度は、プログラムの経路の数から算出されるので、実は、

 循環的複雑度 ≒ C1(分岐網羅)の経路数

と言うことができます。

そのため、循環的複雑度を利用すると、xUnitのテストケース数が、妥当かどうかを評価するための参考になります

3. バグの修正スピードが向上される

過去に循環的複雑度の影響を調査したところ、「複雑度が30以上のコードがいくつもあるプロジェクト」と、「複雑度が30以下を徹底したプロジェクト」では、バグの修正スピードに大きな違いが見られました。

バグの発生から解決までの時間を「バグTAT(Bug Turn Around Time)」と呼んでいるのですが、この値が、前者では3〜5日程度かかったものが、後者では、1〜2日程度となっていました

数プロジェクトでの検証ではあるのですが、構造がシンプルになる分、バグの修正スピードも向上されるようです。
バグ修正の時間が、約半分になることを考えると、大きなメリットですよね。

循環的複雑度を測定するツール

循環的複雑度は、静的解析のひとつに含まれますが、そのメトリクスを測定・活用しているプロジェクトは、あまり見ないですね。

でも、フリーで利用できるツールもたくさんあるので、利用しないのは損ですね。

私は、Javaで開発することが多いので、主に以下のツールを利用しています。

JavaNCSSには、AntタスクやMavenプラグインもあるので、自動レポートにも利用できます。

循環的複雑度は、言語によってツールが違いますが、メジャーな言語ならば、大抵フリーのツールがあるので、利用してみると良いと思います。

最後に

循環的複雑度自体は、新しいものではないのですが、知らない人も多いようです。
自分自身の実感として、循環的複雑度をうまく活用すると、コードの質が非常に上がりますよ。

ぜひ、自分のプロジェクトで試してみてください。