Skip to content

๐Ÿ“˜ Technical Design 008 โ€” Automated Complexity Metrics Enforcement

Field Value
ID TD-008
Title Automated Complexity Metrics Enforcement
Status Completed
Owner Reflection maintainers
Audience Contributors and maintainers
Created 2025-09-30
Last Updated 2025-09-30
Reviewers
Approvers
Tracking
Related 002-refactor-high-fidelity-type-hint-provider task

๐Ÿงญ 1. Summary

This design introduces automated tooling to enforce code complexity metrics across the loryth.common codebase, preventing complexity regression and maintaining code readability standards. The system will integrate with CI/CD pipelines to automatically detect and flag violations of established complexity thresholds.

At a glance:

  • Integrate cyclomatic complexity linting (max 10 per method)
  • Add cognitive complexity measurement (max 15 per method)
  • Enforce method length limits (max 50 lines per method)
  • Monitor nesting depth constraints (max 3 levels)
  • Provide actionable feedback for complexity violations
  • Support incremental adoption across existing codebase

๐Ÿ“š 2. Context and Problem Statement

The loryth.common reflection subsystem has undergone significant refactoring to improve maintainability, including module splitting (TD-004) and upcoming provider refactoring. However, without automated enforcement, complexity can gradually increase over time, leading to:

  • Reduced readability: Complex methods become harder to understand and review
  • Maintenance burden: High complexity increases bug likelihood and debugging time
  • Testing difficulty: Complex code paths are harder to test comprehensively
  • Knowledge silos: Complex code requires domain expertise to modify safely

Currently, complexity assessment relies on manual code review, which is: - Inconsistent: Different reviewers apply different standards - Reactive: Issues are caught after code is written, not prevented - Subjective: Lacks objective, measurable criteria - Time-consuming: Manual complexity assessment slows down reviews

2.1 Goals

  1. Prevent complexity regression: Automatically detect when new code exceeds complexity thresholds
  2. Standardize quality metrics: Establish consistent, objective standards across all contributors
  3. Improve code quality: Encourage writing readable, maintainable code through early feedback
  4. Accelerate reviews: Reduce manual effort in assessing code complexity during reviews
  5. Enable continuous improvement: Track complexity trends over time for informed refactoring decisions
  6. Support incremental adoption: Allow existing code to be gradually improved without blocking new development

2.2 Non-Goals (Out of Scope)

  • Automatic code refactoring: Tool will flag violations but not automatically fix them
  • Performance metrics: Focus is on readability/maintainability, not runtime performance
  • Domain-specific complexity: Will not measure reflection-specific complexity patterns
  • Complete coverage initially: May start with subset of codebase and expand incrementally
  • Custom complexity algorithms: Will use established, well-understood metrics
  • IDE integration: Focus on CI/CD integration first, IDE plugins are future work

2.3 Assumptions and Constraints

  • Python 3.13 environment: Tooling must work with current development setup
  • CI/CD integration: Must integrate with existing Makefile and testing workflows
  • Developer workflow: Should not significantly slow down development cycles
  • Existing codebase: Some legacy code may exceed thresholds temporarily
  • Tool availability: Prefer established, maintained tools over custom implementations
  • Alpha stage: Can introduce breaking changes to development workflow if needed

๐Ÿงฉ 3. Design Overview

The design implements a tool-centric approach to complexity enforcement using established Python tooling integrated into the local development workflow via Makefile targets. The system provides immediate feedback during development through granular make rules following existing project conventions.

The solution combines multiple complementary metrics to provide comprehensive complexity assessment while remaining practical for daily development use.

3.1 Complexity Measurement Tools

Tool Purpose Integration
radon/mccabe Cyclomatic complexity analysis (max 10 per method) make cyclomatic-lint
cognitive-complexity Human-perceived complexity assessment (max 15 per method) make cognitive-lint
Custom AST analyzer Method length validation (max 50 lines per method) make method-length-lint
Custom AST analyzer Nesting depth checking (max 3 levels) make nesting-depth-lint

3.2 Configuration and Thresholds

Centralized Configuration: All thresholds and tool settings managed in pyproject.toml Threshold Standards: Industry-standard limits with exemption support for legacy code Granular Control: Individual tool configuration with file/pattern exclusions Consistent Reporting: Standardized output format across all complexity tools

3.3 Integration Points

Makefile Integration: Following existing project patterns (ruff-lint, mypy-lint) Aggregate Target: make complexity-lint runs all complexity checks Individual Targets: Granular rules for each specific metric Future Extension: Prepared for CI integration when project adds automated workflows

๐Ÿ“ 4. Implementation Details

4.1 Cyclomatic Complexity Enforcement

Tool: radon or mccabe (via flake8-mccabe)

# Makefile target
.PHONY: cyclomatic-lint
cyclomatic-lint:
    @echo -e "\033[0;1;36m[$(name)] Checking cyclomatic complexity...\033[0m"
    $(python) -m radon cc $(source) --min=B --max=10 --exclude="*/__init__.py"

# Configuration (pyproject.toml)
[tool.radon]
cc_min = "B"  # Grade B = complexity 6-10
exclude = "tests/*,docs/*,*/__init__.py"
show_complexity = true
max_complexity = 10

Behavior: - Threshold: Maximum cyclomatic complexity of 10 per method/function - Scope: All source code under src/loryth/common/ - Reporting: Grade-based output (A=1-5, B=6-10, C=11-20, etc.) - Integration: Part of make lint command and CI pipeline

Error handling: - Violations: Exit code 1 when any method exceeds threshold - Reporting: Detailed output showing violating methods and their scores - Exemptions: Configuration-based exclusions for specific files/patterns

4.2 Cognitive Complexity Analysis

Tool: cognitive-complexity or custom implementation

# Makefile target
.PHONY: cognitive-lint
cognitive-lint:
    @echo -e "\033[0;1;36m[$(name)] Checking cognitive complexity...\033[0m"
    $(python) -m cognitive_complexity $(source) --max-complexity 15

# Configuration (pyproject.toml)
[tool.cognitive-complexity]
max_complexity = 15
exclude = ["tests/", "docs/", "*/__init__.py"]

Behavior: - Threshold: Maximum cognitive complexity of 15 per method/function - Scope: Focus on main implementation files, exclude tests initially - Measurement: Counts nested structures, boolean logic, and recursion - Integration: Separate check from cyclomatic complexity

Error handling: - Violations: Clear error messages with specific complexity contributors - Guidance: Suggest refactoring strategies for common violation patterns - Context: Show complexity breakdown by construct type

4.3 Method Length Monitoring

Tool: Custom implementation via AST analysis

# Makefile target
.PHONY: method-length-lint
method-length-lint:
    @echo -e "\033[0;1;36m[$(name)] Checking method length...\033[0m"
    $(python) scripts/check_method_length.py $(source) --max-lines 50

# Configuration (pyproject.toml)
[tool.method-length-checker]
max_lines = 50
exclude_patterns = ["test_*", "__init__"]
count_docstrings = false
count_comments = false

Behavior: - Threshold: Maximum 50 lines per method/function (excluding docstrings/comments) - Scope: All methods and functions in source code - Counting: Logical lines only, excluding whitespace and documentation - Reporting: List of violating methods with actual line counts

4.4 Nesting Depth Validation

Tool: Custom AST analyzer

# Makefile target
.PHONY: nesting-depth-lint
nesting-depth-lint:
    @echo -e "\033[0;1;36m[$(name)] Checking nesting depth...\033[0m"
    $(python) scripts/check_nesting_depth.py $(source) --max-depth 3

# Configuration (pyproject.toml)
[tool.nesting-depth-checker]
max_depth = 3
check_comprehensions = true
exclude_patterns = ["test_*", "__init__"]

Behavior: - Threshold: Maximum 3 levels of nesting (excluding module/class level) - Scope: All control flow structures (if/for/while/try/with) - Analysis: Static analysis via AST traversal - Reporting: Show exact location and depth of violations

โš™๏ธ 5. Semantics

5.1 Metric Calculation Standards

Cyclomatic Complexity: - Each decision point adds 1 to complexity - Base complexity starts at 1 - Boolean operators (and/or) in conditions add 1 each - Exception handlers add 1 per except clause

Cognitive Complexity: - Nested structures increase score more than sequential structures - Recursion and jumps (break/continue) add cognitive load - Boolean logic in conditions contributes to cognitive burden

Method Length: - Count only logical lines (exclude blank lines, comments, docstrings) - Include method signature in line count - Multi-line expressions count as multiple lines

Nesting Depth: - Count indentation levels within methods/functions - Each control structure (if/for/while/try/with) adds one level - List/dict comprehensions count as nesting

5.2 Integration Workflow

Development cycle: 1. Local development: Individual complexity checks via granular make targets 2. Aggregate checking: make complexity-lint runs all complexity tools 3. Integration with existing lint: Enhanced make lint includes complexity checks 4. Reporting: Clear violation messages with actionable guidance

Error handling strategy: - Fail fast: Stop on any threshold violation with clear error messages - Specific guidance: Detailed information on which metrics were violated - Actionable advice: Suggest refactoring patterns for common violations

5.3 Configuration Management

Centralized settings: All thresholds configured in pyproject.toml Environment flexibility: Different thresholds for test vs. production code Exemption handling: File-pattern and method-name based exclusions Threshold evolution: Ability to gradually tighten thresholds over time

๐Ÿงช 6. Examples (concise)

6.1 Makefile Integration

# Enhanced lint target
.PHONY: lint
lint: ruff-lint mypy-lint complexity-lint

# Aggregate complexity target
.PHONY: complexity-lint
complexity-lint: cyclomatic-lint cognitive-lint method-length-lint nesting-depth-lint

# Individual complexity targets
.PHONY: cyclomatic-lint
cyclomatic-lint:
    @echo -e "\033[0;1;36m[$(name)] Checking cyclomatic complexity...\033[0m"
    $(python) -m radon cc $(source) --min=B --max=10

.PHONY: cognitive-lint
cognitive-lint:
    @echo -e "\033[0;1;36m[$(name)] Checking cognitive complexity...\033[0m"
    $(python) -m cognitive_complexity $(source) --max-complexity 15

6.2 Configuration Example

# pyproject.toml
[tool.complexity-enforcement]
cyclomatic_max = 10
cognitive_max = 15
method_length_max = 50
nesting_depth_max = 3

[tool.complexity-enforcement.exclusions]
files = ["tests/*", "docs/*", "*/__init__.py"]
methods = ["test_*", "_test_*"]

[tool.complexity-enforcement.reporting]
output_format = "json"
trend_tracking = true
dashboard_url = "https://complexity-dashboard.internal"

6.3 Violation Reporting

# Example output for violations
โŒ Complexity violations found:

Cyclomatic Complexity:
  src/loryth/common/reflection/high_fidelity_type_hints.py:231
    _resolve_hints_obj: 15 (threshold: 10)

Cognitive Complexity:
  src/loryth/common/reflection/high_fidelity_type_hints.py:547
    _deep_resolve: 18 (threshold: 15)

Method Length:
  src/loryth/common/reflection/high_fidelity_type_hints.py:231
    _resolve_hints_obj: 207 lines (threshold: 50)

๐Ÿ’ก Suggested fixes:
  - Extract nested logic into separate methods
  - Reduce boolean logic complexity in conditions
  - Break large methods into focused helper functions

โœ… 7. Acceptance Criteria

  • Cyclomatic complexity checking integrated with maximum threshold of 10
  • Cognitive complexity analysis implemented with maximum threshold of 15
  • Method length validation enforces 50-line maximum per method
  • Nesting depth checking prevents more than 3 levels of indentation
  • All complexity checks integrated into make lint command
  • Granular Makefile targets for individual complexity checks
  • Ready for future CI integration when project adds automated workflows
  • Clear, actionable error messages for all violation types
  • Configuration centralized in pyproject.toml with exemption support
  • Existing codebase baseline established (violations tracked but not blocking initially)
  • Documentation updated with complexity standards and tooling usage
  • Tool performance impact is minimal (< 30 seconds added to local lint)
  • Complexity trend tracking implemented for continuous improvement
  • Developer workflow documentation updated with complexity checking guidance

๐Ÿš€ 8. Rollout and Migration

Phase 1: Tool Integration

  1. Research and select tools: Evaluate radon, mccabe, cognitive-complexity options
  2. Create configuration: Set up thresholds and exclusions in pyproject.toml
  3. Implement checking scripts: Create Makefile targets following project conventions
  4. Local testing: Validate tools work correctly on current codebase

Phase 2: Baseline Establishment

  1. Audit existing code: Run tools on entire codebase to identify current violations
  2. Document exceptions: Create exemption list for legacy code that exceeds thresholds
  3. Create tracking: Establish baseline metrics for trend analysis
  4. Developer communication: Announce new standards and tooling

Phase 3: Makefile Integration

  1. Add granular targets: Implement individual complexity check targets
  2. Enhance aggregate lint: Update make lint to include complexity checks
  3. Configure reporting: Set up clear violation reporting with actionable guidance
  4. Documentation: Update contributor guides with complexity requirements

Phase 4: Enforcement and Improvement

  1. Monitor compliance: Track adherence to complexity standards
  2. Refactor violations: Gradually address existing codebase violations
  3. Tighten thresholds: Consider reducing thresholds as code quality improves
  4. Expand coverage: Apply complexity checking to additional modules

Future Work: Address Existing Violations

Complexity metrics enforcement is now active but identifies existing violations throughout the codebase. These should be addressed incrementally:

High Priority Violations (HighFidelityTypeHintProvider): - _deep_resolve: Cyclomatic 38, Cognitive 111, Lines 92, Nesting 5 - _resolve_hints_obj: Cyclomatic 36, Cognitive 67, Lines 188, Nesting 4 - _resolve_single_with_augmentation: Cyclomatic 11, Cognitive 19, Lines 84

Medium Priority Violations (Reflection Subsystem): - member_builder.py::_build_type: Cyclomatic 34, Cognitive 91, Lines 127, Nesting 5 - safe_callable_invoker.py::invoke: Cyclomatic 18, Cognitive 41, Lines 63, Nesting 5 - type_checker.py::_matches: Cyclomatic 26, Cognitive 63, Lines 64, Nesting 5 - Various forward reference handling and property resolution methods

Lower Priority Violations: - semantic_version.py::__lt__: Cyclomatic 12, Cognitive 20 - state_machine.py::__init__: Cognitive 23, Nesting 4 - di/extract_dependencies.py::extract_dependencies: Cognitive 20

Recommended Approach: 1. Start with HighFidelityTypeHintProvider refactoring (Task 002 already planned) 2. Address reflection subsystem violations incrementally during regular maintenance 3. Consider raising thresholds temporarily for specific legacy methods if needed

Future Work: CI Integration

When the project adds CI/CD pipelines, complexity checks can be easily integrated by leveraging the existing Makefile targets in automated workflows.

โš ๏ธ 9. Risks and Mitigations

Technical Risks

Risk: Tool performance impact slows down local development - Mitigation: Benchmark tools and optimize for speed; provide granular targets for quick individual checks

Risk: False positives from complexity analysis tools - Mitigation: Establish exemption patterns; fine-tune tool configurations; provide override mechanisms

Risk: Incompatibility with Python 3.13 or existing toolchain - Mitigation: Test tools thoroughly in development environment; have fallback tool options

Adoption Risks

Risk: Developer resistance to additional complexity constraints - Mitigation: Provide clear rationale and guidance; start with warnings before enforcement; show examples of improvement

Risk: Blocking development velocity with strict enforcement - Mitigation: Phase in enforcement gradually; provide clear guidance on addressing violations; maintain reasonable thresholds; granular targets allow checking specific metrics

Risk: Tool maintenance burden and dependency management - Mitigation: Prefer well-maintained, popular tools; have multiple tool options; keep configuration simple

Operational Risks

Risk: Inconsistent enforcement across different development environments - Mitigation: Centralize configuration in pyproject.toml; provide clear setup documentation; Makefile ensures consistent execution

Risk: Complexity threshold drift over time without review - Mitigation: Regular review cycles; trend tracking; documented threshold rationale

๐Ÿ“ˆ 10. Progress

Legend: [x] = complete, [ ] = pending

Phase 1: Tool Integration โœ… COMPLETE

  • Research and evaluate complexity analysis tools
  • Set up cyclomatic complexity checking (radon/mccabe)
  • Implement cognitive complexity analysis
  • Create method length validation
  • Add nesting depth checking
  • Configure pyproject.toml with thresholds and exclusions
  • Create Makefile targets for complexity checks

Phase 2: Baseline and Configuration โœ… COMPLETE

  • Audit existing codebase for current complexity violations
  • Document exemptions for legacy code (via pyproject.toml exclusions)
  • Establish baseline metrics for tracking
  • Configure violation reporting format
  • Test local development workflow

Phase 3: Makefile Integration โœ… COMPLETE

  • Add granular complexity check targets to Makefile
  • Enhance make lint to include complexity checks
  • Configure clear violation reporting
  • Update developer documentation
  • Validate performance impact on local development
  • Prepare for future CI integration

๐Ÿ“ 11. Changelog

  • 2025-09-30 โ€” Initial design based on refactoring task 002 requirements and industry complexity standards
  • 2025-09-30 โ€” Implementation completed: all complexity metrics tooling functional and integrated with Makefile

๐Ÿ“– 12. Glossary

  • Cyclomatic Complexity: Metric measuring the number of decision points in code (if statements, loops, etc.)
  • Cognitive Complexity: Metric assessing how difficult code is for humans to understand, considering nesting and logical complexity
  • Method Length: Count of logical lines of code within a single method or function
  • Nesting Depth: Maximum levels of indentation within control flow structures
  • Static Analysis: Automated code analysis without executing the program
  • Threshold: Maximum allowed value for a complexity metric before violations are flagged
  • Baseline: Current state measurement used as reference point for tracking improvements
  • AST: Abstract Syntax Tree, internal representation of code structure used for analysis