design-smell-detector
Design Smell Detector
Identify and address design quality issues in code through automated smell detection and refactoring guidance.
Quick Start
Detect Design Smells
# Analyze a single file
python scripts/detect_smells.py src/app.py
# Analyze entire directory
python scripts/detect_smells.py src/
# Output as JSON
python scripts/detect_smells.py src/ --format json
Example Output
Found 5 design smell(s): 1 critical, 3 major, 1 minor
CRITICAL ISSUES:
🔴 src/services.py:45 - God Class
Class 'UserManager' has 25 methods (threshold: 20)
💡 Split into multiple smaller, focused classes
MAJOR ISSUES:
🟠 src/models.py:120 - Low Cohesion
Class 'Order' has low cohesion (score: 0.25)
💡 Group related methods/attributes or split class
🟠 src/utils.py:89 - Long Method
Method 'process_order' has 67 lines (threshold: 50)
💡 Extract smaller methods or refactor
Design Smells Detected
Coupling Smells
High Coupling: Too many dependencies
- Module imports > 20
- Constructor dependencies > 10
- Changes cascade across classes
Feature Envy: Method uses other class more than own
- External access > internal access
- Method should move to envied class
Inappropriate Intimacy: Classes too tightly coupled
- Accessing private fields of other classes
- Excessive use of getters/setters
Cohesion Smells
Low Cohesion: Class members unrelated
- LCOM (Lack of Cohesion) > 0.7
- Methods don't share instance variables
- Class has multiple responsibilities
God Class: Class knows/does too much
- Methods > 20
- Attributes > 15
- Lines of code > 500
Complexity Smells
High Cyclomatic Complexity: Too many decision points
- Complexity > 10
- Deeply nested conditionals
- Many if/else, loops
Long Method: Method too long
- Lines of code > 50
- Multiple responsibilities
- Hard to understand
Size Smells
Long Parameter List: Too many parameters
- Parameters > 5
- Related parameters not grouped
- Method signature hard to understand
Large Module: Module too large
- Classes > 20
- Lines of code > 1000
- Multiple responsibilities
Encapsulation Smells
Data Class: Only data, no behavior
- Only getters/setters
- No business logic
- Missing encapsulation
Exposed Internal State: Implementation details exposed
- Public mutable fields
- Returns references to internal collections
- Breaks encapsulation
Detection Workflow
1. Run Detection
Analyze codebase for design smells:
python scripts/detect_smells.py src/
2. Review Results
Examine detected smells by severity:
- Critical: God classes, severe coupling issues
- Major: Low cohesion, long methods, high complexity
- Minor: Long parameter lists, feature envy
3. Prioritize Refactoring
Focus on:
- Critical issues first
- Frequently changed code
- High-impact, low-effort improvements
4. Apply Refactoring
Use refactoring strategies based on smell type.
See refactoring_strategies.md for detailed solutions.
5. Verify Improvements
Re-run detection to measure progress:
python scripts/detect_smells.py src/
Compare metrics before/after.
Common Design Smells
God Class
Symptoms:
- Too many methods (>20)
- Too many attributes (>15)
- Low cohesion
- Multiple responsibilities
Example:
# ❌ God Class
class Application:
# 30+ methods handling:
# - User management
# - Order processing
# - Payment handling
# - Reporting
# - Email notifications
# - File operations
pass
Refactoring:
# ✅ Split by responsibility
class UserManager:
pass
class OrderProcessor:
pass
class PaymentHandler:
pass
class ReportGenerator:
pass
High Coupling
Symptoms:
- Too many imports (>20)
- Too many constructor dependencies
- Changes cascade across classes
Example:
# ❌ High coupling
class UserService:
def __init__(self):
self.db = Database()
self.cache = Cache()
self.logger = Logger()
self.validator = Validator()
self.email = EmailService()
self.sms = SMSService()
# ... many more
Refactoring:
# ✅ Dependency injection
class UserService:
def __init__(self, db, logger, notifier):
self.db = db
self.logger = logger
self.notifier = notifier # Abstraction
Low Cohesion
Symptoms:
- Methods don't share attributes
- Class does unrelated things
- LCOM score > 0.7
Example:
# ❌ Low cohesion
class UserManager:
def create_user(self):
pass
def send_email(self):
pass
def log_activity(self):
pass
def calculate_discount(self):
pass
Refactoring:
# ✅ High cohesion - focused classes
class UserRepository:
def create_user(self):
pass
class EmailService:
def send_email(self):
pass
class ActivityLogger:
def log_activity(self):
pass
Long Method
Symptoms:
- Method > 50 lines
- Multiple responsibilities
- Hard to understand
Example:
# ❌ Long method (100+ lines)
def process_order(order):
# Validate (20 lines)
# Calculate price (15 lines)
# Save to DB (10 lines)
# Send email (20 lines)
# Update stats (10 lines)
# Log activity (15 lines)
pass
Refactoring:
# ✅ Extract methods
def process_order(order):
validate_order(order)
total = calculate_total(order)
save_order(order, total)
send_confirmation(order)
update_statistics()
log_activity(order)
Refactoring Strategies
Reduce Coupling
Dependency Injection:
# Inject dependencies instead of creating them
class OrderService:
def __init__(self, db, logger):
self.db = db
self.logger = logger
Interface Abstraction:
# Depend on abstractions, not implementations
from abc import ABC, abstractmethod
class PaymentGateway(ABC):
@abstractmethod
def charge(self, amount):
pass
class PaymentProcessor:
def __init__(self, gateway: PaymentGateway):
self.gateway = gateway
Improve Cohesion
Extract Class:
# Split into focused classes
class User:
# User data and behavior
class UserRepository:
# Database operations
class EmailService:
# Email operations
Move Method:
# Move method to appropriate class
class Account:
def get_formatted_balance(self): # Moved from reporter
return f"${self.balance:.2f}"
Reduce Complexity
Extract Method:
# Break complex method into smaller ones
def process_order(order):
validate_order(order)
calculate_total(order)
save_order(order)
Replace Conditional with Polymorphism:
# Use inheritance instead of conditionals
class Customer:
def calculate_price(self, base_price):
return base_price
class PremiumCustomer(Customer):
def calculate_price(self, base_price):
return base_price * 0.9
For comprehensive refactoring strategies, see refactoring_strategies.md.
Metrics and Thresholds
Detection Thresholds
| Smell | Metric | Threshold |
|---|---|---|
| God Class | Methods | > 20 |
| God Class | Attributes | > 15 |
| God Class | LOC | > 500 |
| High Coupling | Imports | > 20 |
| Low Cohesion | LCOM | > 0.7 |
| Long Method | LOC | > 50 |
| High Complexity | Cyclomatic | > 10 |
| Long Parameter List | Parameters | > 5 |
Key Metrics
LCOM (Lack of Cohesion of Methods):
- Measures how methods share instance variables
- Range: 0.0 (high cohesion) to 1.0 (low cohesion)
- Lower is better
Cyclomatic Complexity:
- Number of linearly independent paths
- Each if/while/for adds 1
- Lower is better (< 10)
Fan-out (Coupling):
- Number of dependencies
- Lower is better (< 10)
Design Smell Catalog
For complete descriptions, examples, and solutions for all design smells, see smell_catalog.md.
Includes:
- Coupling Smells: High coupling, Feature Envy, Inappropriate Intimacy
- Cohesion Smells: Low cohesion, God Class
- Complexity Smells: High complexity, Long Method
- Size Smells: Long Parameter List, Large Module
- Encapsulation Smells: Data Class, Exposed Internal State
Best Practices
1. Detect Regularly
Integrate into workflow:
# Pre-commit hook
python scripts/detect_smells.py src/
# CI/CD pipeline
make check-design-smells
2. Track Metrics Over Time
Monitor trends:
Sprint 1: 15 God classes, 45 long methods
Sprint 2: 12 God classes, 38 long methods
Sprint 3: 8 God classes, 25 long methods
3. Prioritize Strategically
Focus on:
- Code changed frequently
- Critical business logic
- High-impact areas
4. Refactor Incrementally
- Small, safe changes
- Run tests after each change
- Commit frequently
5. Measure Improvements
Before/after metrics:
Before: LCOM = 0.85 (low cohesion)
After: LCOM = 0.25 (high cohesion)
Common Scenarios
Scenario 1: Legacy Codebase Modernization
Goal: Identify technical debt in legacy system
Approach:
- Run smell detection on entire codebase
- Generate metrics report
- Identify top 10 worst files
- Create refactoring backlog
- Tackle incrementally
Scenario 2: Code Review Enhancement
Goal: Automated design quality checks in PR reviews
Approach:
- Add smell detection to CI pipeline
- Fail build if critical smells introduced
- Report metrics in PR comments
- Require fixes before merge
Scenario 3: Architecture Assessment
Goal: Evaluate system architecture quality
Approach:
- Detect coupling and cohesion issues
- Identify God classes and large modules
- Analyze dependency structure
- Recommend architectural improvements
Integration with Development Workflow
Pre-commit Hook
#!/bin/bash
# .git/hooks/pre-commit
python scripts/detect_smells.py src/
if [ $? -ne 0 ]; then
echo "Critical design smells detected. Commit aborted."
exit 1
fi
CI/CD Pipeline
# .github/workflows/quality.yml
name: Design Quality Check
on: [push, pull_request]
jobs:
check-smells:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Detect design smells
run: python scripts/detect_smells.py src/
- name: Upload report
uses: actions/upload-artifact@v3
with:
name: smell-report
path: smell-report.json
IDE Integration
Most IDEs support similar analysis through plugins:
- PyCharm: Built-in inspections for coupling/cohesion
- VS Code: Python linting extensions
- SonarLint: Real-time smell detection
Troubleshooting
False Positives
Problem: Legitimate design flagged as smell
Solution:
- Understand context and constraints
- Consider if threshold should be adjusted
- Document reasons for exception
Overwhelming Results
Problem: Too many smells to address
Solution:
- Filter by severity (critical first)
- Focus on frequently changed code
- Set incremental goals
Refactoring Breaks Tests
Problem: Tests fail after refactoring
Solution:
- Ensure comprehensive test coverage first
- Refactor incrementally
- Run tests after each small change
Reference Documentation
Design Smell Catalog
See smell_catalog.md for:
- Complete smell definitions
- Detection criteria and thresholds
- Code examples (before/after)
- Metrics explanations (LCOM, cyclomatic complexity, fan-out)
- All coupling, cohesion, complexity, size, and encapsulation smells
Refactoring Strategies
See refactoring_strategies.md for:
- Coupling reduction strategies (DI, interfaces, facades)
- Cohesion improvement (extract class, move method)
- Complexity reduction (extract method, polymorphism)
- Size reduction (split classes, parameter objects)
- Encapsulation improvement
- Refactoring workflow and best practices
- Anti-patterns to avoid
- Success metrics