一般的に条件分岐やループが多ければ多いほどその関数のバグの混入確率が上がると言われています。関数の複雑度を測るために循環的複雑度(cyclomatic-complexity)を用いて複雑度を数値として計測し、閾値を超えていないかをlintで検証する方法を紹介します。
前半部分はほぼ同じ内容ですが、別記事でESLintを使った方法を紹介しています。
ESLintでJavaScriptの循環的複雑度(cyclomatic-complexity)を検証する - Qiita
循環的複雑度(cyclomatic-complexity)とは
一言で言えば、プログラムの複雑度です。分岐の数が多ければプログラムの実行経路は多くなり複雑度は上がります。逆に少なければ下がります。if
やfor
など分岐がないプログラムの循環的複雑度は1となります。
循環的複雑度の数え方は大まかに言えば「if
などの条件分岐やfor
などのループなど制御構文の数」+1した数です。
else
は含みません。
参考: Project Metrics Help - Complexity metrics
循環的複雑度の閾値
非常に古い統計データですが、循環複雑度が高ければバグは発生しやすいということは以下のグラフを見るとわかります。
循環的複雑度 | バグ混入確率 |
---|---|
11 | 25% |
38 | 50% |
74 | 98% |
参考: Cyclomatic Complexity Revisited
循環的複雑度が10を超えるとバグ混入確率がゆるやかに上昇していっているので、1つの関数の循環的複雑度は10以内に抑えるようにコードを書くようにするといいでしょう。
tslintでの検証
TSLintはTypeScriptの静的解析ツールです。
TSLintは様々なルールがありますが、循環的複雑度を検証するルールも存在します。
それがRule: cyclomatic-complexityです。
ルールの設定は簡単で循環的複雑度が10を超える場合はエラーにしたい場合はtslint.jsonに以下のように追記します
{
"cyclomatic-complexity": [true, 10]
}
を追記すれば循環的複雑度の設定は完了です。
設定する値は検証を行うかのフラグと閾値です。
例えば分岐が10ある関数が存在する場合は以下のようにエラーになります。
循環的複雑度の閾値を10に設定した場合、条件分岐が10ある関数があれば以下のようなエラーメッセージを出力します。
ERROR: /path/to/sample-code.ts:1:1 - The function test has a cyclomatic complexity of 11 which is higher thanthe threshold of 10
あとがき
最近ではJavaScriptといえばTypeScriptと言わんばかりにTypeScriptが使われるようになってきていると感じています。
TypeScriptはJavaScriptに比べると型安全やnull安全などバグの混入確率は少なくなったもののロジックが複雜であればバグの混入確率は上がりますし、可読性も下がります。
なので、循環的複雑度を下げるようにするためにもTSLintでチェックすることをおすすめします。
最後までお読みいただきいただきありがとうございました。質問や不備などがあればコメント欄またはTwitter(@shisama_)までお願いいたします。