low-complexity について
関数やメソッドの認知的複雑度と循環的複雑度に目標値を設ける、開発者向けのコーディングスキルです。複雑度の数え方、優先順位付きのリファクタリング手法、分岐やネストを減らす例を示します。
主な機能
- 01関数の複雑度目標の設定
- 02認知的・循環的複雑度の算出
- 03関数抽出とガード節によるリファクタリング
- 04ネストと分岐の削減
- 05言語慣用表現の活用
活用シーン
- 01複雑な関数のレビュー
- 02深くネストしたロジックの整理
- 03コード変更における複雑度基準の設定
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ mryll/ skills --skill low-complexityインストール後、Claude Code で/low-complexityを入力して読み込まれたか確認します。
同梱ファイルreferences/cognitive-complexity-spec.md
詳細
低複雑度のコード
作成または変更するすべての関数/メソッドは、次の目標を満たす必要があります。
- 認知的複雑度は 5 以下(SonarSource の指標)。本質的に複雑なロジックでは、10 まで許容されます。15 を超えてはいけません。
- 循環的複雑度は 5 以下。10 まで許容されます。15 を超えてはいけません。
完全なスコアリングルールについては、cognitive-complexity-spec.mdを参照してください。
認知的複雑度のクイックリファレンス
それぞれ +1: if、三項演算子(? :)、switch(全体)、for、while、do while、catch、else if、else、goto LABEL、break/continue LABEL、再帰サイクル内の各メソッド、同種のブール演算子(&& / ||)の各連続列。
ネストによるペナルティとして +1 を構造上の加算に加算:if、三項演算子、switch、for、while、catch が、別のフロー制御構造の中にネストされている場合。
加算なし: メソッド呼び出し、try、finally、case ラベル、null 合体(?.、??)、早期 return、単純な break/continue、ラムダ(ただしラムダはネストレベルを上げます)。
循環的複雑度のクイックリファレンス
メソッドの開始で +1、次のそれぞれで +1:if、else if、for、while、do while、case、catch、&&、||、三項演算子 ?。これは基本的な定義です。一部の解析ツールでは言語によって異なる場合があります。
必須の削減手法
次の優先順位で適用してください。
- メソッド/関数を抽出する — 一貫性のある処理ブロックを、名前付き関数に移します。ネストレベルは 0 にリセットされます。抽出するブロックが一貫した単位を形成する場合は、まずこの方法を選んでください。
- 早期 return/ガード節 — 条件を反転させて早期に return し、ネストを 1 レベル減らします。
- ネストした条件をフラットなロジックに置き換える —
if A { if B {をif A && B {に置き換えます(ネストによるペナルティを削減します)。 - if/else の連鎖をポリモーフィズム、ストラテジーパターン、またはルックアップテーブルに置き換える — 分岐を完全に解消します。
- ループと条件を宣言的なパイプラインに置き換える —
forとifの代わりに、filter/map/reduce、LINQ、またはストリームを使用します。 - ブール式を分解する — 複雑な条件を、名前付きのブール変数または述語関数として抽出します。
- フラグ変数を早期終了に置き換える — 後続のフロー制御に使われるブールフラグを解消します。
- 言語固有のイディオムを使う — null 合体、オプショナルチェーン、パターンマッチング、分割代入を使用します。これらは、同等の if/else の連鎖よりコストが低いことがよくあります(分割代入は加算なし、パターンマッチングは分岐ごとに +1 の if/else と比べ、match 全体で +1)。
適用方法
関数/メソッドを書くときは、次の手順に従ってください。
- ロジックを書く
- 頭の中で数える:各
if/else if/else/for/while/switch/catch/ternaryは +1、構造上の各ネストレベルはさらに +1、ブール演算子の各連続列は +1 - スコアが 5 を超える場合は、仕上げる前に上記の手法でリファクタリングする
- 大きな関数を 1 つ作るより、小さな関数を複数作る
- ネストの深さが 2 を超える場合は、問題の兆候です。すぐに抽出してください
悪い例と良い例
悪い例:ネストした条件分岐(CogC = 9)
def process(user, order):
if user.is_active: # +1
if order.is_valid: # +2 (nesting=1)
if order.total > 100: # +3 (nesting=2)
apply_discount(order)
else: # +1
charge_full(order)
else: # +1
raise InvalidOrder()
else: # +1
raise InactiveUser() # Total: 1+2+3+1+1+1 = 9
良い例:ガード節と抽出(process CogC=2、charge CogC=2)
def process(user, order): # CogC = 2
if not user.is_active: # +1
raise InactiveUser()
if not order.is_valid: # +1
raise InvalidOrder()
charge(order)
def charge(order): # CogC = 2
if order.total > 100: # +1
apply_discount(order)
else: # +1
charge_full(order)
悪い例:条件をネストしたループ(CogC = 10)
function findFirst(items, criteria) {
for (const item of items) { // +1
if (item.active) { // +2 (nesting=1)
if (item.type === criteria.type) { // +3 (nesting=2)
if (item.score > criteria.min) { // +4 (nesting=3)
return item;
}
}
}
} // Total: 1+2+3+4 = 10
return null;
}
良い例:フラットな filter と早期 continue(findFirst CogC=5、matches CogC=1)
function findFirst(items, criteria) { // CogC = 5
for (const item of items) { // +1
if (!item.active) continue; // +2 (nesting=1)
if (matches(item, criteria)) return item; // +2 (nesting=1)
}
return null;
}
function matches(item, criteria) { // CogC = 1
return item.type === criteria.type // +1 (&& sequence)
&& item.score > criteria.min;
}