Files
claude-howto/03-skills/code-review/scripts/compare-complexity.py
T
Luong NGUYEN 5caeff2f1c refactor: Reorganize repository structure for optimal learning path
Reorder folders based on learning dependencies, complexity, and frequency of use:
- 01-slash-commands (unchanged) - Quick wins for beginners
- 02-memory (was 03) - Essential foundation
- 03-skills (was 05) - Auto-invoked capabilities
- 04-subagents (was 02) - Task delegation
- 05-mcp (was 04) - External integration
- 06-hooks (was 07) - Event automation
- 07-plugins (was 06) - Bundled solutions
- 08-checkpoints (unchanged) - Safe experimentation
- 09-advanced-features (unchanged) - Power user tools

Documentation improvements:
- Add LEARNING-ROADMAP.md with detailed milestones and exercises
- Simplify README.md for better scannability
- Consolidate Quick Start and Getting Started sections
- Combine Feature Comparison and Use Case Matrix tables
- Reorder README sections: Learning Path → Quick Reference → Getting Started
- Update all cross-references across module READMEs

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-09 17:54:58 +01:00

160 lines
5.5 KiB
Python

#!/usr/bin/env python3
"""
Compare cyclomatic complexity of code before and after changes.
Helps identify if refactoring actually simplifies code structure.
"""
import re
import sys
from typing import Dict, Tuple
class ComplexityAnalyzer:
"""Analyze code complexity metrics."""
def __init__(self, code: str):
self.code = code
self.lines = code.split('\n')
def calculate_cyclomatic_complexity(self) -> int:
"""
Calculate cyclomatic complexity using McCabe's method.
Count decision points: if, elif, else, for, while, except, and, or
"""
complexity = 1 # Base complexity
# Count decision points
decision_patterns = [
r'\bif\b',
r'\belif\b',
r'\bfor\b',
r'\bwhile\b',
r'\bexcept\b',
r'\band\b(?!$)',
r'\bor\b(?!$)'
]
for pattern in decision_patterns:
matches = re.findall(pattern, self.code)
complexity += len(matches)
return complexity
def calculate_cognitive_complexity(self) -> int:
"""
Calculate cognitive complexity - how hard is it to understand?
Based on nesting depth and control flow.
"""
cognitive = 0
nesting_depth = 0
for line in self.lines:
# Track nesting depth
if re.search(r'^\s*(if|for|while|def|class|try)\b', line):
nesting_depth += 1
cognitive += nesting_depth
elif re.search(r'^\s*(elif|else|except|finally)\b', line):
cognitive += nesting_depth
# Reduce nesting when unindenting
if line and not line[0].isspace():
nesting_depth = 0
return cognitive
def calculate_maintainability_index(self) -> float:
"""
Maintainability Index ranges from 0-100.
> 85: Excellent
> 65: Good
> 50: Fair
< 50: Poor
"""
lines = len(self.lines)
cyclomatic = self.calculate_cyclomatic_complexity()
cognitive = self.calculate_cognitive_complexity()
# Simplified MI calculation
mi = 171 - 5.2 * (cyclomatic / lines) - 0.23 * (cognitive) - 16.2 * (lines / 1000)
return max(0, min(100, mi))
def get_complexity_report(self) -> Dict:
"""Generate comprehensive complexity report."""
return {
'cyclomatic_complexity': self.calculate_cyclomatic_complexity(),
'cognitive_complexity': self.calculate_cognitive_complexity(),
'maintainability_index': round(self.calculate_maintainability_index(), 2),
'lines_of_code': len(self.lines),
'avg_line_length': round(sum(len(l) for l in self.lines) / len(self.lines), 2) if self.lines else 0
}
def compare_files(before_file: str, after_file: str) -> None:
"""Compare complexity metrics between two code versions."""
with open(before_file, 'r') as f:
before_code = f.read()
with open(after_file, 'r') as f:
after_code = f.read()
before_analyzer = ComplexityAnalyzer(before_code)
after_analyzer = ComplexityAnalyzer(after_code)
before_metrics = before_analyzer.get_complexity_report()
after_metrics = after_analyzer.get_complexity_report()
print("=" * 60)
print("CODE COMPLEXITY COMPARISON")
print("=" * 60)
print("\nBEFORE:")
print(f" Cyclomatic Complexity: {before_metrics['cyclomatic_complexity']}")
print(f" Cognitive Complexity: {before_metrics['cognitive_complexity']}")
print(f" Maintainability Index: {before_metrics['maintainability_index']}")
print(f" Lines of Code: {before_metrics['lines_of_code']}")
print(f" Avg Line Length: {before_metrics['avg_line_length']}")
print("\nAFTER:")
print(f" Cyclomatic Complexity: {after_metrics['cyclomatic_complexity']}")
print(f" Cognitive Complexity: {after_metrics['cognitive_complexity']}")
print(f" Maintainability Index: {after_metrics['maintainability_index']}")
print(f" Lines of Code: {after_metrics['lines_of_code']}")
print(f" Avg Line Length: {after_metrics['avg_line_length']}")
print("\nCHANGES:")
cyclomatic_change = after_metrics['cyclomatic_complexity'] - before_metrics['cyclomatic_complexity']
cognitive_change = after_metrics['cognitive_complexity'] - before_metrics['cognitive_complexity']
mi_change = after_metrics['maintainability_index'] - before_metrics['maintainability_index']
loc_change = after_metrics['lines_of_code'] - before_metrics['lines_of_code']
print(f" Cyclomatic Complexity: {cyclomatic_change:+d}")
print(f" Cognitive Complexity: {cognitive_change:+d}")
print(f" Maintainability Index: {mi_change:+.2f}")
print(f" Lines of Code: {loc_change:+d}")
print("\nASSESSMENT:")
if mi_change > 0:
print(" ✅ Code is MORE maintainable")
elif mi_change < 0:
print(" ⚠️ Code is LESS maintainable")
else:
print(" ➡️ Maintainability unchanged")
if cyclomatic_change < 0:
print(" ✅ Complexity DECREASED")
elif cyclomatic_change > 0:
print(" ⚠️ Complexity INCREASED")
else:
print(" ➡️ Complexity unchanged")
print("=" * 60)
if __name__ == '__main__':
if len(sys.argv) != 3:
print("Usage: python compare-complexity.py <before_file> <after_file>")
sys.exit(1)
compare_files(sys.argv[1], sys.argv[2])