code-quality-master
Maestro de calidad de código - refactoring, code smells, arquitectura limpia y mejores prácticas
When & Why to Use This Skill
The Code Quality Master skill is a comprehensive tool designed to audit, maintain, and enhance software codebases. It specializes in identifying 'code smells,' implementing automated refactoring patterns, and enforcing SOLID principles and Clean Architecture. By providing deep analysis of cyclomatic complexity and maintainability indices, it helps developers eliminate technical debt and ensure high-performance, readable, and scalable code.
Use Cases
- Automated detection and refactoring of 'Code Smells' such as God Classes, Long Methods, and Feature Envy to improve code readability.
- Applying SOLID principles and Clean Code guidelines to legacy codebases to transform them into maintainable and modular systems.
- Calculating and monitoring software quality metrics, including Cyclomatic Complexity and the Maintainability Index, for data-driven optimization.
- Restructuring complex conditional logic using polymorphism and extracting classes to adhere to the Single Responsibility Principle.
- Providing architectural recommendations and modularization strategies for large-scale projects (e.g., splitting monolithic files into service-oriented structures).
| name | code-quality-master |
|---|---|
| description | Maestro de calidad de código - refactoring, code smells, arquitectura limpia y mejores prácticas |
Code Quality Master Skill
Skill especializado en mantener y mejorar la calidad del código a través de detección de code smells, refactoring automático, y aplicación de principios de arquitectura limpia.
Principios de Calidad
SOLID Principles
S - Single Responsibility: Una clase, una razón para cambiar
O - Open/Closed: Abierto para extensión, cerrado para modificación
L - Liskov Substitution: Subtipos sustituibles por tipos base
I - Interface Segregation: Interfaces específicas > interfaces grandes
D - Dependency Inversion: Depender de abstracciones, no de concretos
Clean Code Guidelines
# 1. Nombres significativos
# MALO
def calc(d, u):
return (u / d) * 100
# BUENO
def calculate_usage_rate(granted_days: float, used_days: float) -> float:
return (used_days / granted_days) * 100
# 2. Funciones pequeñas (< 20 líneas)
# 3. Un nivel de abstracción por función
# 4. Comentarios solo cuando el código no puede explicarse
# 5. Manejo de errores separado de lógica de negocio
Detección de Code Smells
Catálogo de Smells
1. Long Method (> 50 líneas)
# Detección
def detect_long_methods(file_content: str, threshold: int = 50) -> list:
"""Detecta métodos con más de N líneas."""
issues = []
for func in extract_functions(file_content):
if func['line_count'] > threshold:
issues.append({
'type': 'LONG_METHOD',
'name': func['name'],
'lines': func['line_count'],
'suggestion': 'Extract smaller methods with single responsibility'
})
return issues
# Refactoring: Extract Method
# ANTES
def process_employees(employees):
# 100 líneas de código
pass
# DESPUÉS
def process_employees(employees):
validated = validate_employees(employees)
enriched = enrich_with_metadata(validated)
return save_to_database(enriched)
2. God Class (> 20 métodos o > 500 líneas)
# ANTES: App class con 50+ métodos
class App:
def init_data(self): ...
def fetch_employees(self): ...
def render_table(self): ...
def render_charts(self): ...
def handle_click(self): ...
# ... 45 métodos más
# DESPUÉS: Separar en clases especializadas
class DataService:
def fetch_employees(self): ...
def sync_data(self): ...
class UIManager:
def render_table(self): ...
def render_charts(self): ...
class EventHandler:
def handle_click(self): ...
3. Feature Envy
# MALO: Método que usa más datos de otra clase
def calculate_employee_stats(employee):
return {
'usage_rate': (employee.used / employee.granted) * 100,
'is_compliant': employee.used >= 5,
'days_remaining': employee.granted - employee.used
}
# BUENO: Mover lógica a la clase Employee
class Employee:
def get_usage_rate(self) -> float:
return (self.used / self.granted) * 100
def is_compliant(self) -> bool:
return self.used >= 5
@property
def days_remaining(self) -> float:
return self.granted - self.used
4. Data Clumps
# MALO: Grupos de datos que siempre van juntos
def create_employee(name, employee_num, hire_date, department, factory):
...
def update_employee(employee_id, name, employee_num, hire_date, department, factory):
...
# BUENO: Agrupar en objetos
@dataclass
class EmployeeInfo:
name: str
employee_num: str
hire_date: date
department: str
factory: str
def create_employee(info: EmployeeInfo):
...
5. Primitive Obsession
# MALO: Usar primitivos para conceptos de dominio
def process_leave_request(employee_num: str, days: float, leave_type: str):
if leave_type not in ['full', 'half_am', 'half_pm', 'hourly']:
raise ValueError("Invalid leave type")
# BUENO: Usar tipos específicos
from enum import Enum
class LeaveType(Enum):
FULL = 'full'
HALF_AM = 'half_am'
HALF_PM = 'half_pm'
HOURLY = 'hourly'
@dataclass
class LeaveRequest:
employee_num: str
days: float
leave_type: LeaveType
Matriz de Severidad
| Smell | Severidad | Impacto en Mantenibilidad |
|---|---|---|
| Long Method | 🟠 MEDIUM | -20% readability |
| God Class | 🔴 HIGH | -40% maintainability |
| Feature Envy | 🟡 LOW | -10% cohesion |
| Data Clumps | 🟠 MEDIUM | -15% clarity |
| Primitive Obsession | 🟡 LOW | -10% type safety |
| Duplicate Code | 🔴 HIGH | -50% DRY |
| Dead Code | 🟠 MEDIUM | -25% clarity |
| Magic Numbers | 🟡 LOW | -15% clarity |
Refactoring Patterns
1. Extract Class
# ANTES
class Employee:
def __init__(self):
self.name = ""
self.birth_date = ""
self.address = ""
self.city = ""
self.postal_code = ""
self.hire_date = ""
self.granted_days = 0
self.used_days = 0
def get_full_address(self):
return f"{self.address}, {self.city} {self.postal_code}"
def get_balance(self):
return self.granted_days - self.used_days
# DESPUÉS
@dataclass
class Address:
street: str
city: str
postal_code: str
def __str__(self):
return f"{self.street}, {self.city} {self.postal_code}"
@dataclass
class VacationBalance:
granted: float
used: float
@property
def remaining(self) -> float:
return self.granted - self.used
@dataclass
class Employee:
name: str
birth_date: date
hire_date: date
address: Address
vacation: VacationBalance
2. Replace Conditional with Polymorphism
# ANTES
def calculate_days(employee_type: str, seniority: float) -> int:
if employee_type == 'fulltime':
return get_fulltime_days(seniority)
elif employee_type == 'parttime':
return get_parttime_days(seniority)
elif employee_type == 'contract':
return get_contract_days(seniority)
# DESPUÉS
from abc import ABC, abstractmethod
class EmployeeType(ABC):
@abstractmethod
def calculate_days(self, seniority: float) -> int:
pass
class FullTimeEmployee(EmployeeType):
def calculate_days(self, seniority: float) -> int:
return get_fulltime_days(seniority)
class PartTimeEmployee(EmployeeType):
def calculate_days(self, seniority: float) -> int:
return get_parttime_days(seniority)
3. Introduce Parameter Object
# ANTES
def search_employees(
name: str = None,
department: str = None,
factory: str = None,
year: int = None,
min_balance: float = None,
max_balance: float = None,
is_compliant: bool = None,
sort_by: str = None,
sort_order: str = None,
page: int = 1,
page_size: int = 50
):
pass
# DESPUÉS
@dataclass
class EmployeeSearchCriteria:
name: str = None
department: str = None
factory: str = None
year: int = None
min_balance: float = None
max_balance: float = None
is_compliant: bool = None
@dataclass
class PaginationOptions:
page: int = 1
page_size: int = 50
sort_by: str = None
sort_order: str = 'asc'
def search_employees(
criteria: EmployeeSearchCriteria,
pagination: PaginationOptions = None
):
pass
Métricas de Calidad
Complejidad Ciclomática
def calculate_cyclomatic_complexity(func_ast) -> int:
"""
CC = 1 + número de puntos de decisión
Puntos de decisión:
- if, elif, else
- for, while
- try, except
- and, or
- case (match)
"""
complexity = 1
for node in ast.walk(func_ast):
if isinstance(node, (ast.If, ast.For, ast.While, ast.ExceptHandler)):
complexity += 1
elif isinstance(node, ast.BoolOp):
complexity += len(node.values) - 1
return complexity
# Thresholds
COMPLEXITY_THRESHOLDS = {
'GOOD': (1, 10),
'MODERATE': (11, 20),
'HIGH': (21, 50),
'VERY_HIGH': (51, float('inf'))
}
Maintainability Index
def calculate_maintainability_index(
halstead_volume: float,
cyclomatic_complexity: int,
loc: int
) -> float:
"""
MI = 171 - 5.2 * ln(V) - 0.23 * G - 16.2 * ln(LOC)
Where:
- V = Halstead Volume
- G = Cyclomatic Complexity
- LOC = Lines of Code
Score interpretation:
- 85+: Highly maintainable
- 65-84: Moderately maintainable
- <65: Difficult to maintain
"""
import math
mi = 171 - 5.2 * math.log(halstead_volume) \
- 0.23 * cyclomatic_complexity \
- 16.2 * math.log(loc)
return max(0, min(100, mi))
Code Coverage Quality
COVERAGE_QUALITY_RULES = {
'line_coverage': {
'min': 70,
'good': 80,
'excellent': 90
},
'branch_coverage': {
'min': 60,
'good': 75,
'excellent': 85
},
'function_coverage': {
'min': 80,
'good': 90,
'excellent': 95
}
}
Análisis Automático
Output de Análisis
{
"file": "main.py",
"metrics": {
"loc": 5058,
"functions": 145,
"classes": 8,
"avg_complexity": 4.2,
"max_complexity": 32,
"maintainability_index": 72
},
"code_smells": [
{
"type": "LONG_METHOD",
"location": "main.py:234-350",
"name": "sync_all_data",
"lines": 116,
"suggestion": "Extract into smaller methods: parse_excel, validate_data, save_to_db"
},
{
"type": "DUPLICATE_CODE",
"locations": ["main.py:400-420", "main.py:500-520"],
"similarity": 0.92,
"suggestion": "Extract common logic to shared function"
}
],
"refactoring_opportunities": [
{
"pattern": "EXTRACT_METHOD",
"function": "get_employees_by_type",
"benefit": "Reduce complexity from 15 to 5",
"risk": "LOW"
}
],
"quality_score": "B",
"recommendations": [
"Split main.py into multiple modules (routes/, services/)",
"Add type hints to 45 functions missing them",
"Remove 12 unused imports"
]
}
Integración con YuKyuDATA
Refactorings Prioritarios
1. Modularizar main.py
# Estructura propuesta
app/
├── main.py # Solo setup de FastAPI
├── routes/
│ ├── employees.py # Endpoints de empleados
│ ├── leave_requests.py # Endpoints de solicitudes
│ ├── compliance.py # Endpoints de compliance
│ └── analytics.py # Endpoints de analytics
├── services/
│ ├── employee_service.py
│ ├── leave_service.py
│ └── compliance_service.py
├── models/
│ ├── employee.py
│ ├── leave_request.py
│ └── compliance.py
└── utils/
├── validators.py
└── formatters.py
2. Aplicar Domain Objects
# Antes: Diccionarios por todos lados
employee = {
'employee_num': '001',
'name': 'Tanaka',
'granted': 10,
'used': 5,
'year': 2025
}
# Después: Domain models
from pydantic import BaseModel
class Employee(BaseModel):
employee_num: str
name: str
granted: float
used: float
year: int
@property
def balance(self) -> float:
return self.granted - self.used
@property
def usage_rate(self) -> float:
if self.granted == 0:
return 0
return (self.used / self.granted) * 100
def is_compliant_with_5day_rule(self) -> bool:
return self.granted < 10 or self.used >= 5
Comandos de Análisis
# Python
# Complexity analysis
pip install radon
radon cc main.py -a -s # Cyclomatic complexity
radon mi main.py -s # Maintainability index
radon hal main.py # Halstead metrics
# Linting
pip install pylint flake8 mypy
pylint main.py --output-format=json
flake8 main.py --format=json
mypy main.py --json
# JavaScript
# ESLint with complexity
npx eslint static/js/app.js --format=json
# Code duplication
pip install pylint
pylint --disable=all --enable=duplicate-code *.py
# Dead code
pip install vulture
vulture . --min-confidence 80
Principio Guía: "El código limpio es código que otros desarrolladores pueden leer, entender y modificar con confianza."