Módulo 7: Modernizar Legacy Code

Identificar Tech Debt con Claude Code

Identificar Tech Debt con Claude Code

Descripción de la cápsula

Antes de modernizar, necesitas un inventario: ¿qué tech debt tiene este código? Claude Code puede escanear un módulo completo y listar code smells, patterns deprecated, dead code, imports no usados, y falta de type hints — en minutos. Manualmente, este audit toma horas.

En esta cápsula vas a aprender a ejecutar tech debt scans sistemáticos con Claude Code, categorizar findings por tipo, y priorizarlos con la matriz impacto/riesgo.


Los 6 Tipos de Tech Debt

1. Syntax Deprecated

# Old Python:
name = "Hello, %s" % user_name           # %-formatting
items = dict([(k, v) for k, v in data])   # dict comprehension verbose
if type(x) == int:                         # type() comparison
file = open("data.txt")                    # no context manager

# Modern Python:
name = f"Hello, {user_name}"              # f-strings
items = {k: v for k, v in data}           # dict comprehension
if isinstance(x, int):                     # isinstance()
with open("data.txt") as file:            # context manager

2. Missing Type Hints

# Sin type hints (ambiguo):
def calculate_total(items, discount, tax_rate):
    subtotal = sum(i["price"] * i["qty"] for i in items)
    return subtotal * (1 - discount) * (1 + tax_rate)

# Con type hints (claro):
def calculate_total(
    items: list[dict[str, float]],
    discount: float,
    tax_rate: float
) -> float:
    subtotal = sum(i["price"] * i["qty"] for i in items)
    return subtotal * (1 - discount) * (1 + tax_rate)

3. Dead Code

import os           # nunca se usa
import json         # nunca se usa
from datetime import timedelta  # nunca se usa

def old_calculate_tax(amount):  # nunca se llama
    """Deprecated: use calculate_tax_v2"""
    return amount * 0.16

LEGACY_URL = "https://old-api.example.com"  # nunca se referencia

4. Deprecated Patterns

# Pattern viejo: manual error handling
try:
    file = open("config.json")
    data = json.load(file)
    file.close()
except:                          # bare except (atrapa todo)
    pass                         # silencia errores

# Pattern moderno: context manager + specific exception
try:
    with open("config.json") as file:
        data = json.load(file)
except FileNotFoundError:
    data = {}
except json.JSONDecodeError as e:
    logger.error(f"Invalid config: {e}")
    data = {}

5. Code Duplication

# En user_service.py:
tax = subtotal * 0.16
if region == "EU":
    tax = subtotal * 0.21

# En order_service.py (idéntico):
tax = subtotal * 0.16
if region == "EU":
    tax = subtotal * 0.21

# En invoice_service.py (idéntico):
tax = subtotal * 0.16
if region == "EU":
    tax = subtotal * 0.21

6. Deprecated Dependencies

# requirements.txt con deps deprecated:
flask==1.1.4        # EOL, debería ser 3.x
requests==2.25.0    # viejo, usar httpx o actualizar
python-jose==3.3.0  # sin mantenimiento, usar PyJWT

Scan Sistemático con Claude Code

El prompt de tech debt scan

> "Analiza [archivo o módulo] y genera un inventario
   completo de tech debt. Para cada item, reporta:
   1. Tipo (syntax, type hints, dead code, pattern, duplication, dependency)
   2. Ubicación (archivo:línea)
   3. Severidad (alta/media/baja)
   4. Descripción (qué es y por qué es tech debt)
   5. Fix sugerido (cómo modernizarlo)
   
   Organiza por tipo y severidad."

Output esperado

# Tech Debt Inventory: src/services/order_service.py

## Syntax Deprecated (3 items)
| # | Línea | Severidad | Descripción | Fix |
|---|-------|-----------|-------------|-----|
| 1 | 23 | Baja | %-formatting | f-string |
| 2 | 45 | Baja | dict() con list comprehension | dict comprehension |
| 3 | 67 | Media | open() sin context manager | with statement |

## Missing Type Hints (5 items)
| # | Línea | Severidad | Descripción | Fix |
|---|-------|-----------|-------------|-----|
| 1 | 12 | Media | create_order() sin type hints | Agregar hints |
| 2 | 34 | Media | calculate_total() sin hints | Agregar hints |
| ... | ... | ... | ... | ... |

## Dead Code (2 items)
| # | Línea | Severidad | Descripción | Fix |
|---|-------|-----------|-------------|-----|
| 1 | 5 | Baja | import os (no usado) | Eliminar |
| 2 | 89 | Media | old_validate() nunca llamada | Eliminar |

## Deprecated Patterns (2 items)
| # | Línea | Severidad | Descripción | Fix |
|---|-------|-----------|-------------|-----|
| 1 | 67 | Media | bare except | Specific exceptions |
| 2 | 78 | Alta | SQL string concatenation | Parameterized query |

## Total: 12 items (2 alta, 5 media, 5 baja)

Priorización con Matriz Impacto/Riesgo

La matriz

Alto impactoBajo impacto
Bajo riesgo✅ PRIMERO🔄 Cuando sea conveniente
Alto riesgo⚠️ Planificar❌ Probablemente no vale

Aplicando la matriz

✅ PRIMERO (alto impacto, bajo riesgo):
  - Dead code removal
  - Import cleanup
  - bare except → specific exceptions

🔄 CUANDO SEA CONVENIENTE (bajo impacto, bajo riesgo):
  - %-formatting → f-strings
  - dict() verbose → dict comprehension

⚠️ PLANIFICAR (alto impacto, alto riesgo):
  - SQL concatenation → parameterized (seguridad)
  - Missing type hints en funciones públicas

❌ PROBABLEMENTE NO (bajo impacto, alto riesgo):
  - Reescribir funciones que funcionan "por estética"

Conexión con Proyecto

En el Proyecto del Módulo (cápsula 05), el primer paso es un tech debt scan completo. La priorización determina el orden de modernización.


Troubleshooting

Problema 1: Claude Code reporta demasiados items

Solución: Filtra por severidad. Enfócate en alta y media. Los de baja son "nice to have."

Problema 2: No sé si algo es realmente tech debt

Solución: Pregunta: "¿Este código causaría problemas en una code review de 2026?" Si sí, es tech debt. Si funciona bien y es legible, puede estar bien.

Problema 3: El equipo no usa type hints

Solución: No introduzcas type hints en un módulo si el resto del proyecto no los usa. Modernización debe ser alineada con el equipo.


Ejercicios

Ejercicio 1: Clasificar tech debt (Fácil)

Clasifica cada item por tipo y severidad:

  1. import sys que nunca se usa
  2. except: sin especificar excepción
  3. Función de 300 líneas con 5 responsabilidades
  4. "Hello %s" % name en vez de f-string
  5. Query SQL con string concatenation: f"SELECT * FROM users WHERE id = {user_id}"
Ver solución
  1. Dead code, Baja — no afecta funcionalidad
  2. Deprecated pattern, Media — puede ocultar errores
  3. Code smell, Alta — difícil de mantener y testear
  4. Syntax deprecated, Baja — funcional, solo cosmético
  5. Security vulnerability, MUY ALTA — SQL injection posible

Ejercicio 2: Escribir prompt de scan (Medio)

Escribe el prompt para que Claude Code escanee src/services/ completo buscando solo patterns deprecated y dead code.

Ver solución
> "Escanea todos los archivos en src/services/ buscando:
   1. Dead code: funciones nunca llamadas, imports no usados,
      variables no referenciadas
   2. Patterns deprecated: bare except, open() sin with,
      manual file.close(), %-formatting, type() comparison
   
   Para cada finding, reporta: archivo, línea, tipo,
   y fix sugerido. Ignora type hints y code style."

Errores Comunes en Tech Debt Scans

Error 1: Confundir "no me gusta" con "tech debt"

Síntoma: Tu lista incluye "este nombre es feo" o "yo lo escribiría diferente". El equipo rechaza el PR.

Por qué pasa: Preferencias estéticas se cuelan como "tech debt". Pero tech debt es costo objetivo: bugs futuros, dificultad de mantenimiento, deuda de seguridad — no estilo personal.

Cómo corregir: Para cada item, pregúntate: "¿este código causaría problemas en una code review profesional, o solo me molesta?". Si solo molesta, no es debt — es preferencia.

Error 2: Severidad uniforme ("todo es importante")

Síntoma: 30 items, 30 marcados como severidad media-alta. La priorización es imposible.

Por qué pasa: Cada item se siente importante en el momento. Pero priorizar requiere diferenciar — y eso significa marcar la mayoría como baja.

Cómo corregir: Distribución típica saludable: 10% alta, 30% media, 60% baja. Si todo es alto, nada es alto. SQL injection es alta. F-string vs %-formatting es baja, no media.

Error 3: Marcar "dead code" sin verificación dinámica

Síntoma: Eliminaste 3 funciones "muertas". En producción, una se llama desde un cron job. Incident.

Por qué pasa: Confianza ciega en grep. Python tiene callbacks dinámicos, decorators con strings, getattr, plugins, entry points en setup.py.

Cómo corregir: Antes de marcar dead, verifica:

  1. grep -r "function_name" en TODO el repo (no solo el módulo)
  2. Búsqueda en strings: grep -r '"function_name"'
  3. Busca en config files (YAML, TOML, JSON)
  4. Si hay logs accesibles, verifica que no aparece en últimos 30 días
  5. Si todavía dudas, márcalo como "candidate for removal" — no eliminado todavía

Error 4: Buscar tech debt sin entender el contexto del proyecto

Síntoma: Marcaste "no usa async" como tech debt en un proyecto sync-first. El equipo se ríe.

Por qué pasa: Aplicaste un patrón "moderno" sin verificar si encaja en el proyecto. No todo proyecto necesita ser async, type-hinted, o dataclass-based.

Cómo corregir: Antes del scan, lee CLAUDE.md o el README. ¿Qué patrones usa el proyecto? La modernización debe alinearse con el proyecto, no imponer un estilo extranjero.

Error 5: No incluir security en el scan

Síntoma: Tu lista tiene 30 items cosméticos pero no detecta SQL injection, secrets hardcodeados, o validación faltante.

Por qué pasa: El instinto es buscar "código viejo". Pero el peor tech debt es el de seguridad — y se ve más sutil que un f-string.

Cómo corregir: Incluye explícitamente en tu prompt: "Identifica también: SQL string concatenation, hardcoded secrets/passwords/keys, validación faltante en inputs externos, manejo de errores que filtra info sensible". Severidad por defecto: alta o crítica.


Resumen

  • 6 tipos de tech debt: syntax, type hints, dead code, patterns, duplication, dependencies
  • Claude Code escanea en minutos lo que manualmente toma horas
  • Prioriza con impacto/riesgo: dead code primero, cosmético después
  • Tech debt no es negligencia — es evolución natural
  • El scan es el input de la modernización (cápsulas 03-04)
  • Diferencia preferencia de debt — no todo lo que te molesta es deuda
  • Verifica dinámicamente dead code antes de marcar
  • Incluye security explícitamente en el scan

Próxima cápsula: Modernizar Syntax y Patterns Deprecated — ejecutar los fixes.


Recursos Adicionales

  1. pylint - Linter que detecta muchos code smells
  2. vulture - Dead code finder para Python
  3. pyupgrade - Modernización automática de syntax
  4. bandit - Scanner de seguridad para Python
  5. Refactoring Guru - Code Smells - Catálogo completo
  6. SonarQube - Plataforma de análisis de calidad de código