循環的複雑度(CC)は、メソッド内の線形独立な実行パスをカウントすることで、メソッドの構造的複雑度を測定するコード指標です。グラフ理論から派生したこの指標は、メソッドを制御フローグラフとして表し、ノードをステートメントブロック、エッジをそれらの間のジャンプとして表現します。実用的なC#開発において、これは通常、メソッドボディ内の分岐構造の数に1を加えたものとして計算されます。これには、`if`、`while`、`for`、`foreach`、`case`、`default`、`continue`、`goto`といったキーワードに加え、`&&`や`||`などの論理演算子、および`?:`や`??`のような三項演算子が含まれます。

CCは独立したパスの数を表すため、テスト可能性の直接的な代理指標となります。CCスコアが高いメソッドは、すべての可能な分岐をカバーするために多くのテストケースを必要とするため、本質的に読みやすく、テストしやすく、安全に変更することが困難です。普遍的な「完璧な」スコアはありませんが、業界標準では一般的なベンチマークが提供されています。Thomas McCabeは元々10という制限を推奨し、MicrosoftのCA1502アナライザーは25を超えるスコアを過度なものとしてフラグを立てます。この指標を監視する目的は、「バグの磁石」を特定することです。これは、絡み合ったロジックのために単一のデグレードが容易に本番環境に紛れ込んでしまう可能性のある、複雑なメソッドを指します。

C#における循環的複雑度とコードカバレッジの関係を示すチャート
C#プロジェクトにおいて、循環的複雑度が要求されるコードカバレッジとどのように相関するかを示す可視化。blog.ndepend.com

記事では、複雑度のスコアが高いほど完全なパスカバレッジを確保するために多くのテストケースが必要になるため、CCがテスト可能性の直接的な代理指標として機能することを説明しています。

高い複雑度を管理するには、多くの場合、単一の大きなメソッドを、いくつかの小さく焦点を絞ったメソッドにリファクタリングすることが含まれます。例えば、CCが8のメソッドは、検証や割引ロジックなどの特定のタスクを処理するサブメソッドに分割できます。このリファクタリングによって、コードベース全体の複雑度の総計はわずかに増加するかもしれませんが、個々のコードユニットが開発者が一度に推論できるほど単純に保たれることを保証することで、保守性が向上します。これを大規模に管理するために、開発者はNDependやVisual Studioの「コードメトリクスの計算」などのツールを使用してホットスポットを特定できます。これらのスコアをツリーマップ(長方形のサイズがステートメント数を表し、色がCCスコアを表す)で可視化することで、チームはアーキテクチャ的な注意が必要な、巨大で複雑度の高いメソッドを迅速に発見できます。

C#の循環的複雑度スコアのツリーマップ可視化
長方形のサイズがステートメント数を表し、色が循環的複雑度スコアを示すツリーマップの可視化。blog.ndepend.com

テキストでは、ホットスポットを特定するために、サイズがステートメント数を表し、色がCCスコアを表すツリーマップを使用してスコアを可視化することを説明しています。

しかし、循環的複雑度には現代のオブジェクト指向プログラミングにおいて顕著な限界があります。この指標は明示的な分岐に焦点を当てているため、ポリモーフィズムによって導入される暗黙的な複雑度を考慮しません。例えば、インターフェース上の仮想メソッドを呼び出すと、基礎となる実装に応じて数十の異なる実行パスにつながる可能性がありますが、これはCCスコアを増加させません。その結果、CCは複雑な手続き型ロジックを特定するための優れたツールではありますが、システムの保守性の完全な全体像を提供するためには、ネストの深さやテストカバレッジなどの他の指標と組み合わせて使用する必要があります。