Code Complexity Management
Maintain exceptional code quality standards using PHPInsights in this Symfony-based microservice. This skill ensures complexity stays manageable while preserving hexagonal architecture, DDD patterns, and CQRS design.
When to Use This Skill
Use this skill when:
- PHPInsights checks fail (
make phpinsightsreturns errors) - Cyclomatic complexity exceeds thresholds
- Code quality score drops below 100%
- Architecture score falls below 100%
- Style score drops below 100%
- Complexity score falls below thresholds (93% for src/, 95% for tests/)
- Adding new features that increase complexity
- Refactoring existing code for better maintainability
- Before committing code changes (proactive quality check)
⚡ Quick Start
New to complexity refactoring? Start with the Quick Start Guide for a fast-track workflow with immediate action steps.
Protected Quality Thresholds
CRITICAL: These thresholds are defined in phpinsights.php (for src/) and phpinsights-tests.php (for tests/) and must NEVER be lowered:
// phpinsights.php (source code in src/)
'requirements' => [
'min-quality' => 100, // Code quality
'min-complexity' => 93, // Cyclomatic complexity for src/
'min-architecture' => 100, // Architecture compliance
'min-style' => 100, // Coding style
],
// phpinsights-tests.php (test code in tests/)
'requirements' => [
'min-quality' => 95, // Code quality for tests
'min-complexity' => 95, // Cyclomatic complexity for tests/
'min-architecture' => 90, // Architecture compliance for tests
'min-style' => 95, // Coding style for tests
],
Policy: If PHPInsights fails, fix the code - NEVER lower these thresholds.
⚠️ CRITICAL POLICY: NEVER CHANGE CONFIG
╔═══════════════════════════════════════════════════════════════╗
║ When PHPInsights fails, you MUST FIX THE CODE. ║
║ NEVER lower thresholds in phpinsights.php. ║
║ ║
║ ❌ FORBIDDEN: Changing config to pass checks ║
║ ✅ REQUIRED: Refactoring code to meet standards ║
╚═══════════════════════════════════════════════════════════════╝
Protected thresholds are non-negotiable:
For source code (src/):
- min-quality: 100% (never lower)
- min-complexity: 93% (never lower)
- min-architecture: 100% (never lower)
- min-style: 100% (never lower)
For tests (tests/):
- min-quality: 95% (never lower)
- min-complexity: 95% (never lower)
- min-architecture: 90% (never lower)
- min-style: 95% (never lower)
If you're tempted to lower a threshold, it means the code needs refactoring, not the config.
Core Workflow
1. Identify Complex Classes
First, find which classes need refactoring:
# Find top 20 most complex classes (default)
make analyze-complexity
# Find top 10 classes
make analyze-complexity N=10
# Export as JSON for tracking
make analyze-complexity-json N=20 > complexity-report.json
# Export as CSV for spreadsheets
make analyze-complexity-csv N=15 > complexity.csv
Output shows for each class:
- CCN (Cyclomatic Complexity): > 15 is critical
- WMC (Weighted Method Count): Sum of all method complexities
- Avg Complexity: CCN ÷ Methods (target: < 5)
- Max Complexity: Highest complexity of any single method
- Maintainability Index: 0-100 (target: > 65)
See: analysis-tools.md for complete tools documentation
2. Run PHPInsights Analysis
# Using project make command (recommended)
make phpinsights
# Direct execution with verbosity
vendor/bin/phpinsights -v
# Analyze specific directory
vendor/bin/phpinsights analyse src/Customer
# Generate JSON report for parsing
vendor/bin/phpinsights --format=json > insights-report.json
3. Interpret Results
PHPInsights provides four scores:
[CODE] 100.0 pts ✅ Target: 100%
[COMPLEXITY] 93.5 pts ✅ Target: 93%+ (src), 95%+ (tests)
[ARCHITECTURE] 100 pts ✅ Target: 100%
[STYLE] 100.0 pts ✅ Target: 100%
If any score is below threshold, identify issues:
# Look for issues by severity
✗ [Complexity] Method `CustomerCommandHandler::handle` has cyclomatic complexity of 12
✗ [Architecture] Class `CustomerRepository` violates layer dependency rules
✗ [Style] Line exceeds 100 characters in CustomerTransformer.php:45
4. Prioritize Fixes
Priority Order:
- CRITICAL (Complexity > 15): Immediate refactoring required
- HIGH (Architecture violations): Breaks hexagonal/DDD boundaries
- MEDIUM (Complexity 10-15): Plan refactoring
- LOW (Style issues): Quick fixes, often auto-fixable
5. Apply Fixes
Based on issue type:
For Complexity Issues → See refactoring-strategies.md For Metric Details → See reference/complexity-metrics.md For Common Errors → See reference/troubleshooting.md
6. Verify Improvements
# Re-run PHPInsights
make phpinsights
# Verify all thresholds pass
✅ Code quality: 100%
✅ Complexity: 93%+ (src), 95%+ (tests)
✅ Architecture: 100%
✅ Style: 100%
Quick Fixes by Issue Type
Cyclomatic Complexity Too High
Problem: Method has too many decision points (if/else/switch/loops)
First, identify which classes need refactoring:
# Find top 10 most complex classes
make analyze-complexity N=10
Solutions:
- Extract methods: Break complex method into smaller private methods
- Strategy pattern: Replace conditionals with polymorphism
- Early returns: Reduce nesting with guard clauses
- Command pattern: Separate command handling logic
See refactoring-strategies.md for DDD/CQRS-specific patterns.
Architecture Violations
Problem: Layer dependencies violated (e.g., Domain depending on Infrastructure)
Solutions:
- Review layer boundaries: Domain → Application → Infrastructure
- Use interfaces: Define contracts in Domain, implement in Infrastructure
- Dependency injection: Inject dependencies through constructors
- Repository pattern: Keep data access in Infrastructure layer
Project Architecture:
src/{BoundedContext}/
├── Domain/ # No external dependencies
│ ├── Entity/
│ ├── ValueObject/
│ └── Repository/ # Interfaces only
├── Application/ # Depends on Domain
│ ├── Command/
│ ├── CommandHandler/
│ └── Event/
└── Infrastructure/ # Depends on Domain + Application
└── Repository/ # Implementations
Style Issues
Problem: Code doesn't meet PSR-12 or Symfony coding standards
Solutions:
# Auto-fix most style issues
make phpcsfixer
# Re-run PHPInsights to verify
make phpinsights
Line Length > 100 Characters
Problem: Lines exceed configured limit (100 chars)
Config:
LineLengthSniff::class => [
'lineLimit' => 100,
'ignoreComments' => true,
],
Solutions:
- Break long method calls into multiple lines
- Extract complex expressions into variables
- Use named parameters (PHP 8+)
- Refactor long argument lists into DTOs
Project-Specific Configuration
Excluded Sniffs
These sniffs are intentionally disabled in phpinsights.php:
'remove' => [
UnusedParameterSniff::class, // Allow unused params in interfaces
SuperfluousInterfaceNamingSniff::class, // Allow "Interface" suffix
SuperfluousExceptionNamingSniff::class, // Allow "Exception" suffix
SpaceAfterNotSniff::class, // Symfony style preference
ForbiddenSetterSniff::class, // Allow setters where needed
UseSpacingSniff::class, // Symfony style preference
],
Do NOT create issues for these patterns - they're explicitly allowed.
Excluded Files
Some files are excluded from specific checks:
ForbiddenNormalClasses::class => [
'exclude' => [
'src/Shared/Infrastructure/Bus/Command/InMemorySymfonyCommandBus',
'src/Shared/Infrastructure/Bus/Event/InMemorySymfonyEventBus',
'src/Core/Customer/Domain/Entity/Customer',
],
],
These are intentionally not marked final - architectural decision.
Integration with Other Skills
- quality-standards skill: Broader quality enforcement including Psalm, Deptrac
- ci-workflow skill: PHPInsights runs as part of CI checks
- testing-workflow skill: Maintain test coverage while refactoring
- database-migrations skill: Ensure entity changes maintain architecture compliance
Common Patterns in This Codebase
Command Handler Complexity
Command handlers should have low complexity:
// ✅ GOOD: Low complexity (2-3)
final readonly class CreateCustomerCommandHandler implements CommandHandlerInterface
{
public function __construct(
private CustomerRepository $repository,
private DomainEventPublisher $publisher
) {}
public function __invoke(CreateCustomerCommand $command): void
{
$customer = Customer::create(
$command->id,
$command->name,
$command->email
);
$this->repository->save($customer);
$this->publisher->publish(...$customer->pullDomainEvents());
}
}
If complexity is high: Business logic likely in wrong layer - move to Domain.
Domain Entity Complexity
Domain entities can have higher complexity for business rules:
// ✅ Acceptable: Complexity in domain logic
class Customer extends AggregateRoot
{
public function updateSubscription(SubscriptionPlan $plan): void
{
// Complex validation logic is appropriate here
$this->validateSubscriptionChange($plan);
$this->applyDiscount($plan);
$this->record(new SubscriptionChanged($this->id, $plan));
}
}
If too complex: Extract to Domain Service or Value Object.
Success Criteria
After running this skill, verify:
✅ make phpinsights passes without errors
✅ Code quality: 100%
✅ Complexity: ≥ 93% (src), ≥ 95% (tests)
✅ Architecture: 100%
✅ Style: 100%
✅ No layer boundary violations (checked by Deptrac)
✅ All tests still pass (make unit-tests, make integration-tests)
✅ Code remains aligned with hexagonal architecture
Additional Resources
Quick References
- ⚡ Quick Start: quick-start.md - Fast-track workflow for immediate action
- 🔧 Analysis Tools: analysis-tools.md - Complete guide to complexity analysis commands
Detailed Guides
- Refactoring Patterns: refactoring-strategies.md - Modern PHP patterns with real-world examples
- Understanding Metrics: reference/complexity-metrics.md - What each metric means
- Troubleshooting: reference/troubleshooting.md - Common issues and solutions
- Monitoring: reference/monitoring.md - Track improvements over time
External Resources
- PHPInsights Documentation: https://phpinsights.com/
- Project Architecture: See CLAUDE.md for hexagonal/DDD/CQRS patterns