ADR-008: サイクロマティック複雑度制限¶
ステータス¶
承認済み
コンテキスト¶
コードの複雑性を管理し、テスタビリティと保守性を向上させるため、サイクロマティック複雑度の上限を設定する必要がある。
サイクロマティック複雑度は、プログラムの複雑性を測定する指標で、以下の要素をカウントする: - 条件分岐(if, else if, else) - ループ(for, while, do-while) - case文 - 論理演算子(&&, ||)
決定¶
サイクロマティック複雑度の上限を7に設定する。
ESLintのcomplexity
ルールを使用して、この制限を自動的に検証する。
rules: {
'complexity': ['error', { max: 7 }]
}
根拠¶
複雑度7を選択した理由¶
- テスタビリティの確保
- 複雑度7以下の関数は、すべてのパスを網羅するテストケースが現実的に作成可能
-
複雑度が高いほど、必要なテストケース数が指数関数的に増加
-
認知的負荷の軽減
- 人間が一度に理解できる複雑性には限界がある
-
複雑度7は、多くの研究で推奨される上限値(5-10の範囲内)
-
リファクタリングの促進
- 複雑度が高い関数は、単一責任の原則に違反している可能性が高い
- 制限により、開発者は自然と関数を小さく分割するようになる
複雑度と品質の関係¶
複雑度 | コードの品質 | リスクレベル |
---|---|---|
1-3 | シンプル | 低 |
4-7 | 普通 | 中 |
8-10 | 複雑 | 高 |
11+ | 非常に複雑 | 非常に高 |
結果¶
ポジティブな結果¶
- コードの可読性向上: 複雑な関数が小さな単位に分割される
- テストカバレッジの向上: すべてのパスをテストしやすくなる
- バグの削減: 複雑度が低いコードはバグが少ない傾向がある
- 保守性の向上: 将来の変更が容易になる
ネガティブな結果¶
- 初期の開発速度: リファクタリングに時間がかかる可能性
- 関数数の増加: より多くの小さな関数が必要になる
実装例¶
Before(複雑度が高い)¶
function processGame(state: GameState, action: Action): GameState {
if (action.type === 'MOVE') {
if (state.currentPuyo) {
if (action.direction === 'left') {
if (canMoveLeft(state)) {
// 移動処理
}
} else if (action.direction === 'right') {
if (canMoveRight(state)) {
// 移動処理
}
} else if (action.direction === 'down') {
if (canMoveDown(state)) {
// 移動処理
}
}
}
} else if (action.type === 'ROTATE') {
// 回転処理
}
// さらに続く...
}
After(複雑度を下げた)¶
function processGame(state: GameState, action: Action): GameState {
switch (action.type) {
case 'MOVE':
return handleMove(state, action)
case 'ROTATE':
return handleRotate(state, action)
default:
return state
}
}
function handleMove(state: GameState, action: MoveAction): GameState {
if (!state.currentPuyo) return state
const moveHandlers = {
left: moveLeft,
right: moveRight,
down: moveDown,
}
const handler = moveHandlers[action.direction]
return handler ? handler(state) : state
}