Skip to content

feat: Implement SeparationOfConcernsAssessor #78

@jeremyeder

Description

@jeremyeder

feat: Implement SeparationOfConcernsAssessor

Attribute Definition

Attribute ID: separation_of_concerns (Attribute #17 - Tier 2)

Definition: Organizing code so each module/file/function has single, well-defined responsibility (SOLID principles).

Why It Matters: 2 of 5 SOLID principles derive directly from separation of concerns. Clear boundaries improve testability, maintainability, and reduce cognitive load.

Impact on Agent Behavior:

  • Targeted modifications without affecting unrelated code
  • Better refactoring suggestions
  • Clearer module purpose understanding
  • Reduced side effect risk

Measurable Criteria:

  • Each module/class has one reason to change
  • High cohesion within modules (related functions together)
  • Low coupling between modules (minimal dependencies)
  • Organize by feature/domain, not technical layer (avoid separate "controllers", "services", "models" directories)

Implementation Requirements

File Location: src/agentready/assessors/structure.py

Class Name: SeparationOfConcernsAssessor

Tier: 2 (Critical)

Default Weight: 0.03 (3% of total score)

Assessment Logic

Scoring Approach: Heuristic checks for common anti-patterns

Evidence to Check (score components):

  1. Directory organization (40%)

    • Feature-based structure (good): auth/, users/, billing/
    • Layer-based structure (bad): models/, views/, controllers/
    • Check for directories named: models, views, controllers, services, utils, helpers
  2. File length as cohesion indicator (30%)

    • Files >500 lines likely have multiple responsibilities
    • Count files exceeding threshold
    • Penalize repositories with many large files
  3. Import analysis (30%)

    • Check for circular dependencies (Python)
    • Count cross-module imports (high coupling)
    • Look for "God modules" imported by many others

Scoring Logic:

org_score = 100 if feature_based else 60
length_score = 100 - (oversized_files / total_files) * 100
import_score = 100 - (circular_deps * 20)  # Each circular dep -20

total_score = (org_score * 0.4) + (length_score * 0.3) + (import_score * 0.3)

status = "pass" if total_score >= 75 else "fail"

Anti-Patterns to Detect:

  • Directories: models/, views/, controllers/, services/, utils/
  • Files: utils.py, helpers.py, common.py (catch-all modules)
  • Large files: >500 lines
  • Circular imports (Python)

Code Pattern to Follow

Reference: StandardLayoutAssessor in structure.py

Pattern:

  1. Examine directory structure for layer-based anti-patterns
  2. Check file sizes across repository
  3. For Python: Parse imports to detect circular dependencies
  4. Calculate score based on multiple factors
  5. Provide actionable remediation

Example Finding Responses

Pass (Score: 88)

Finding(
    attribute=self.attribute,
    status="pass",
    score=88.0,
    measured_value="feature-based organization",
    threshold="feature-based with cohesive modules",
    evidence=[
        "Feature-based directory structure detected",
        "Average file length: 187 lines (good cohesion)",
        "2/45 files exceed 500 lines (acceptable)",
        "No circular dependencies detected",
    ],
    remediation=None,
    error_message=None,
)

Fail (Score: 52)

Finding(
    attribute=self.attribute,
    status="fail",
    score=52.0,
    measured_value="layer-based organization",
    threshold="feature-based with cohesive modules",
    evidence=[
        "Layer-based directories detected: models/, views/, controllers/",
        "8/45 files exceed 500 lines (poor cohesion)",
        "Catch-all modules found: utils.py, helpers.py",
        "2 circular import cycles detected",
    ],
    remediation=self._create_remediation(),
    error_message=None,
)

Not Applicable

Finding.not_applicable(
    self.attribute,
    reason="Cannot assess: insufficient code files (<5)"
)

Registration

Add to src/agentready/services/scanner.py in create_all_assessors():

from ..assessors.structure import (
    StandardLayoutAssessor,
    GitignoreCompletenessAssessor,
    OneCommandSetupAssessor,
    SeparationOfConcernsAssessor,  # Add this import
)

def create_all_assessors() -> List[BaseAssessor]:
    return [
        # ... existing assessors ...
        SeparationOfConcernsAssessor(),  # Add this line
    ]

Testing Guidance

Test File: tests/unit/test_assessors_structure.py

Test Cases to Add:

  1. test_separation_pass_feature_based: Feature-organized repo scores well
  2. test_separation_fail_layer_based: Layer-organized repo fails
  3. test_separation_fail_large_files: Repository with many 500+ line files
  4. test_separation_detect_circular_imports: Python repo with circular imports
  5. test_separation_not_applicable: Repository with <5 code files

Note: AgentReady uses feature-based organization (assessors/, models/, services/), should score well.

Dependencies

External Tools: None (uses file system inspection and AST parsing)

Python Standard Library:

  • pathlib.Path for directory inspection
  • ast for import analysis (Python only)
  • os.walk() for recursive file traversal

Remediation Steps

def _create_remediation(self) -> Remediation:
    return Remediation(
        summary="Refactor code to improve separation of concerns",
        steps=[
            "Reorganize from layer-based to feature-based structure",
            "Break down large files (>500 lines) into focused modules",
            "Extract catch-all modules (utils, helpers) into specific domains",
            "Eliminate circular dependencies",
            "Group related functionality together",
            "Apply Single Responsibility Principle to modules",
        ],
        tools=[],
        commands=[
            "# Find large files",
            "find src/ -name '*.py' -exec wc -l {} + | sort -rn | head -20",
            "",
            "# Detect circular imports (Python)",
            "pip install pydeps",
            "pydeps src/ --show-cycles",
        ],
        examples=[
            """# Good: Feature-based organization
src/
├── auth/
│   ├── login_service.py
│   ├── oauth_provider.py
│   └── session_manager.py
├── users/
│   ├── user_model.py
│   ├── user_service.py
│   └── user_repository.py
└── billing/
    ├── payment_processor.py
    └── invoice_generator.py
""",
            """# Bad: Layer-based organization
src/
├── models/
│   ├── user.py
│   ├── order.py
│   └── payment.py
├── services/
│   ├── user_service.py
│   ├── order_service.py
│   └── payment_service.py
└── utils/
    └── helpers.py  # Catch-all
""",
        ],
        citations=[
            Citation(
                source="Wikipedia",
                title="Separation of concerns",
                url="https://en.wikipedia.org/wiki/Separation_of_concerns",
                relevance="Foundational principle of software design",
            ),
            Citation(
                source="DevIQ",
                title="Separation of Concerns",
                url="https://deviq.com/principles/separation-of-concerns",
                relevance="Practical guide to applying SoC",
            ),
        ],
    )

Implementation Notes

  1. Directory Detection: Check for common layer-based directory names
  2. File Length: Calculate via line count, threshold at 500 lines
  3. Import Analysis: Parse Python imports with AST, build dependency graph
  4. Circular Dependencies: Use graph traversal to detect cycles
  5. Catch-all Modules: Flag files named utils.py, helpers.py, common.py
  6. Edge Cases: Small repositories (<5 files) return not_applicable
  7. Language-Specific: Full import analysis only for Python, heuristics for others

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions