🌀

Rustプログラムの複雑度を可視化する:サイクロマティック複雑度の測定と閾値設定

に公開

こんにちは、ファスト株式会社のYTです!

「脳に収まるコードの書き方」という本を読み、「サイクロマティック複雑度」を計測しようというプラクティスが興味深かったので、Rust製のプロジェクトに応用する際にどのように応用できそうか調査しました。

サイクロマティック複雑度とは

コードのなかの分岐の数を計測し、メソッドの複雑さを教えてくれます。

「脳に収まるコードの書き方」 p257 付録Aより引用

1つの関数内での分岐や、ループが増えるほど大きくなる値です。

以下、CCNと略します。

本書では、7を閾値とすることを提案していましたが、

  • Rustのプロジェクトにそのまま閾値として使用できそうか
  • プロジェクトの最初から設定する値で妥当そうな値はどれくらいか
  • プロジェクトの途中から設定する値で妥当そうな値はどれくらいか
  • プロジェクトの種類によって変わるのか

という観点で調査しました。

Rust製OSSのプロジェクトを測定してみた

lizardというツールで測定しました。

使用方法は以下の記事を参考にさせていただきました。

https://zenn.dev/qt6hy/articles/950d05c020b587

以下のプロジェクトを測定させていただきました。

計測する際に使用したコマンドは以下の通りです。
$ lizard -l rust --CCN={上限値} -L=100000

計測では、

  • 7: 本書で提案されていた値
  • 15: lizardでデフォルトで警告が出る値
  • 40: 15 × 2 + α

の3種類の閾値を設定し、約何パーセントの関数、何個の関数が閾値を超えているかを計測しました。

計測結果は長くなるため、サマリーだけを記載しています。

deno

denoはJavaScript, TypeScript, WebAssemblyのランタイムです。
言語処理系のプログラムであるため、複雑な関数が多いと予想します。

CCN7以上の計測

==========================================================================================
Total nloc   Avg.NLOC  AvgCCN  Avg.token   Fun Cnt  Warning cnt   Fun Rt   nloc Rt
------------------------------------------------------------------------------------------
    293325      27.8     2.7      141.7     6391          400      0.06    0.26

400 / 6391 ≒ 6.6% の関数が閾値超過となりました。

CCN15以上の計測

==========================================================================================
Total nloc   Avg.NLOC  AvgCCN  Avg.token   Fun Cnt  Warning cnt   Fun Rt   nloc Rt
------------------------------------------------------------------------------------------
    293325      27.8     2.7      141.7     6391          121      0.02    0.15

121 / 6391 ≒ 1.9% の関数が閾値超過となりました。

CCN40以上の計測

==========================================================================================
Total nloc   Avg.NLOC  AvgCCN  Avg.token   Fun Cnt  Warning cnt   Fun Rt   nloc Rt
------------------------------------------------------------------------------------------
    293325      27.8     2.7      141.7     6391           12      0.00    0.05

12 / 6391 ≒ 0.2% の関数が閾値超過となりました。

alacritty

Rust製のターミナルエミュレーターです。
デスクトップアプリケーションの場合はどのくらいの複雑度になるのでしょうか?

CCN7以上の計測

==========================================================================================
Total nloc   Avg.NLOC  AvgCCN  Avg.token   Fun Cnt  Warning cnt   Fun Rt   nloc Rt
------------------------------------------------------------------------------------------
     25443      14.8     2.5      117.3     1000           56      0.06    0.34

56 / 1000 ≒ 5.6% の関数が閾値超過となりました。

CCN15以上の計測

==========================================================================================
Total nloc   Avg.NLOC  AvgCCN  Avg.token   Fun Cnt  Warning cnt   Fun Rt   nloc Rt
------------------------------------------------------------------------------------------
     25443      14.8     2.5      117.3     1000            8      0.01    0.19

8 / 1000 ≒ 0.8% の関数が閾値超過となりました。

プロジェクト開始時からCIで注視すれば0にできそうですね。

CCN40以上の計測

==========================================================================================
Total nloc   Avg.NLOC  AvgCCN  Avg.token   Fun Cnt  Warning cnt   Fun Rt   nloc Rt
------------------------------------------------------------------------------------------
     25443      14.8     2.5      117.3     1000            2      0.00    0.16

2 / 1000 ≒ 0.2% の関数が閾値超過となりました。

ripgrep

Rust製のCLIツールです。
CLIツールの場合はどれくらいになるのでしょうか。

CCN7以上の計測

==========================================================================================
Total nloc   Avg.NLOC  AvgCCN  Avg.token   Fun Cnt  Warning cnt   Fun Rt   nloc Rt
------------------------------------------------------------------------------------------
     38093      13.3     1.9       70.1     1208           39      0.03    0.18

39 / 1208 ≒ 3.2% の関数が閾値超過となりました。

CCN15以上の計測

==========================================================================================
Total nloc   Avg.NLOC  AvgCCN  Avg.token   Fun Cnt  Warning cnt   Fun Rt   nloc Rt
------------------------------------------------------------------------------------------
     38093      13.3     1.9       70.1     1208            6      0.00    0.07

6 / 1208 ≒ 0.5% の関数が閾値超過となりました。

プロジェクト開始時からCIで注視すれば0にできそうですね。

CCN40以上の計測

==========================================================================================
Total nloc   Avg.NLOC  AvgCCN  Avg.token   Fun Cnt  Warning cnt   Fun Rt   nloc Rt
------------------------------------------------------------------------------------------
     38093      13.3     1.9       70.1     1208            1      0.00    0.01

1 / 1208 ≒ 0.0% の関数が閾値超過となりました。

まとめ

主観になりますが、以下のように感じました。

  • デスクトップアプリケーションやCLIツールのようなアプリケーション開発であれば7か15を目安にすると良さそう。
  • プロジェクトの途中から導入する場合は40を目安に導入し、徐々に値を下げていくことで徐々に複雑さを下げていくことができそう。
  • 言語処理系のプログラムはアプリケーションよりも複雑度が高そう。

新しくRustでプログラムを作る際に、閾値を7に設定して導入してみようと思いました!

参考文献

FAST Tech Blog

Discussion