Módulo 7: Subagents para Debugging, Regenerar vs Editar

Subagents Explore para Investigar

Subagents Explore para Investigar

Descripción de la cápsula

Hay un patrón que separa a los developers que usan AI efectivamente de los que no: investigar antes de actuar. Cuando enfrentas un codebase desconocido o un problema complejo, tu primer instinto no debería ser "pídele a Claude Code que lo arregle." Debería ser "pídele a Claude Code que me explique cómo funciona esto."

Claude Code tiene capacidades de exploración que te permiten navegar archivos, seguir imports, mapear dependencias, y entender la arquitectura de un proyecto — todo antes de cambiar una sola línea. Es como tener un colega senior que ya leyó todo el codebase y puede explicarte cualquier parte en 30 segundos.

Pero la clave está en la formulación. "Explícame el código" produce respuestas genéricas. "Muéstrame cómo fluye un request desde el endpoint POST /api/tasks hasta que se guarda en la base de datos" produce un mapa preciso que puedes usar para tomar decisiones. En esta cápsula aprendes a formular queries de investigación que producen información accionable.


Investigar vs Actuar: La Diferencia Fundamental

El anti-patrón: actuar sin investigar

Escenario: Recibes un bug report — "Los permisos no funcionan bien"

Developer sin investigación previa:
1. Abre el archivo que cree que es el problema
2. Le pide a Claude Code: "Fix the permissions bug"
3. Claude Code cambia algo
4. El bug sigue o aparece uno nuevo
5. Repite 3 veces más
6. 45 minutos después, sigue sin resolver

¿Por qué falla?
→ Ni tú ni Claude Code entienden cómo funciona el sistema de permisos
→ El "fix" es un parche que no ataca la causa raíz
→ Cada intento puede romper algo más

El patrón correcto: investigar, entender, actuar

Mismo escenario: "Los permisos no funcionan bien"

Developer con investigación previa:
1. Le pide a Claude Code: "Explica cómo funciona el sistema de 
   permisos en este proyecto. ¿Dónde se definen los roles? ¿Dónde 
   se verifican? ¿Qué middleware aplica?"
2. Claude Code navega el codebase, encuentra 4 archivos relevantes
3. Ahora sabes: roles en models.py, verificación en dependencies.py,
   middleware en middleware.py, y la config en settings.py
4. Con ese mapa, diagnosticas: "El middleware verifica roles pero
   el endpoint no pasa el role del token al middleware"
5. Editas 3 líneas en dependencies.py
6. 15 minutos total, fix de raíz

¿Por qué funciona?
→ La investigación te da un mapa del sistema
→ Con el mapa, el diagnóstico es preciso
→ El fix ataca la causa raíz, no un síntoma

La diferencia no es la herramienta — es el paso previo de comprensión.


Capacidades de Exploración de Claude Code

Qué puede hacer Claude Code cuando investiga

Cuando le pides a Claude Code que explore tu codebase, puede:

  • ✅ Navegar archivos — Abrir, leer, y analizar cualquier archivo del proyecto
  • ✅ Seguir imports — Trazar de dónde viene cada módulo, clase, o función
  • ✅ Mapear dependencias — Identificar qué archivos dependen de cuáles
  • ✅ Explicar flujos — Seguir la ejecución desde un punto A hasta un punto B
  • ✅ Encontrar patrones — Buscar todos los lugares donde se usa cierta función, clase, o patrón
  • ✅ Analizar estructura — Explicar cómo está organizado el proyecto

Qué NO puede hacer

  • ❌ Ejecutar código — No puede correr tu aplicación para ver qué pasa
  • ❌ Ver estado en runtime — No puede ver variables, conexiones de DB, o estado de memoria
  • ❌ Acceder a servicios externos — No puede hacer requests a tu API o base de datos
  • ❌ Ver historial de git completo — Puede ver los archivos actuales pero no puede recorrer todo el historial de commits

El modelo mental correcto

Piensa en Claude Code como un investigador que tiene acceso a todos los documentos de un caso pero no puede ir a la escena del crimen:

Investigador (Claude Code):
├── Puede leer todos los archivos del caso (código fuente)
├── Puede conectar pistas entre documentos (seguir imports)
├── Puede mapear relaciones (dependencias)
├── Puede crear un informe (explicación del flujo)
│
└── NO puede ir a la escena (ejecutar código)
    NO puede interrogar testigos (runtime state)
    NO puede recrear los hechos (reproducir bugs)

Es una herramienta de análisis estático avanzado, no de debugging en runtime. Eso lo hace perfecto para investigación previa a cambios.


Tipos de Investigación y Cuándo Usar Cada Uno

Tipo 1: Entender un flujo completo

Cuándo: Necesitas entender cómo funciona una feature de principio a fin.

Query ejemplo:

Explica el flujo completo de autenticación en este proyecto.
Desde que el usuario envía sus credenciales hasta que recibe 
un token JWT. ¿Qué archivos están involucrados? ¿Qué funciones 
se llaman en qué orden?

Lo que obtienes: Un mapa secuencial del flujo con archivos y funciones involucradas.

Ejemplo de respuesta útil de Claude Code:

Flujo de autenticación:

1. POST /api/auth/login  (routers/auth.py:23)
   → Recibe LoginRequest(email, password)
   
2. AuthService.authenticate()  (services/auth_service.py:15)
   → Busca usuario por email en DB
   → Verifica password con bcrypt
   
3. TokenService.create_token()  (services/token_service.py:8)
   → Crea JWT con user_id, role, exp
   → Usa SECRET_KEY de config/settings.py
   
4. Response: {"access_token": "...", "token_type": "bearer"}

Archivos involucrados:
- routers/auth.py        → Endpoints
- services/auth_service.py → Lógica de auth
- services/token_service.py → Generación de tokens
- models/user.py          → Modelo User con password hash
- config/settings.py      → SECRET_KEY, ALGORITHM, EXPIRATION

Tipo 2: Encontrar todos los usos de un componente

Cuándo: Necesitas entender el impacto de cambiar algo.

Query ejemplo:

Encuentra todos los lugares en este proyecto donde se verifica
el rol del usuario (role checking, permission checking). Incluye
middleware, decoradores, funciones de dependencia, y verificaciones
inline en endpoints.

Lo que obtienes: Una lista exhaustiva de puntos donde se verifica autorización.

Aplicación real:

# Claude Code encuentra estos 6 puntos de verificación de roles:

# 1. dependencies/auth.py - Dependency injection para endpoints
async def get_current_user(token: str = Depends(oauth2_scheme)):
    payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
    return payload

# 2. dependencies/auth.py - Verificación de admin
async def require_admin(user: dict = Depends(get_current_user)):
    if user.get("role") != "admin":
        raise HTTPException(status_code=403, detail="Admin required")

# 3. middleware/auth_middleware.py - Middleware global
class AuthMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request, call_next):
        if request.url.path in self.public_paths:
            return await call_next(request)
        # verifica token...

# 4. routers/users.py:45 - Verificación inline
@router.delete("/users/{user_id}")
async def delete_user(user_id: int, current_user = Depends(get_current_user)):
    if current_user["role"] != "admin" and current_user["id"] != user_id:
        raise HTTPException(403)

# 5. routers/tasks.py:78 - Verificación de ownership
@router.patch("/tasks/{task_id}")
async def update_task(task_id: int, current_user = Depends(get_current_user)):
    task = get_task(task_id)
    if task.owner_id != current_user["id"] and current_user["role"] != "admin":
        raise HTTPException(403)

# 6. services/report_service.py:12 - Verificación en servicio
def generate_report(user_role: str, report_type: str):
    if report_type == "financial" and user_role != "admin":
        raise PermissionError("Financial reports require admin role")

Ahora sabes que hay 6 puntos de verificación de roles dispersos en 5 archivos, con 3 patrones diferentes (dependency injection, middleware, inline). Si necesitas cambiar cómo funcionan los permisos, sabes exactamente dónde mirar.

Tipo 3: Mapear dependencias de un cambio

Cuándo: Quieres cambiar algo y necesitas saber qué más se afecta.

Query ejemplo:

Si cambio la estructura del modelo User en models/user.py 
(agregar un campo 'department'), ¿qué otros archivos necesito 
modificar? ¿Qué schemas, endpoints, y servicios usan este modelo?

Lo que obtienes: Un impact analysis antes de hacer el cambio.

Impacto de agregar campo 'department' a User:

Archivos que necesitan cambio:
├── models/user.py         → Agregar columna department
├── schemas/user.py        → Agregar field a UserCreate, UserResponse
├── migrations/             → Nueva migración de Alembic
├── routers/users.py       → Actualizar endpoint de creación
└── services/user_service.py → Actualizar lógica de creación

Archivos que podrían necesitar cambio:
├── routers/admin.py       → Si filtras usuarios por departamento
├── services/report_service.py → Si reportes agrupan por departamento
└── tests/test_users.py    → Actualizar fixtures y assertions

Archivos que NO se afectan:
├── routers/auth.py        → Auth no usa department
├── routers/tasks.py       → Tasks depende de user_id, no department
└── middleware/             → Middleware no accede a department

Esto es una inversión de 2 minutos que ahorra 30 minutos de descubrir dependencias rotas después de hacer el cambio.

Tipo 4: Investigar un bug antes de diagnosticar

Cuándo: Tienes un bug y necesitas entender el contexto antes de intentar arreglarlo.

Query ejemplo:

Tengo un bug: POST /api/tasks devuelve 500 cuando el usuario 
es admin. Funciona bien para usuarios regulares. Antes de buscar 
el fix, necesito entender:
1. ¿Cómo se crea un task? (flujo completo)
2. ¿Hay diferencia en el path de código entre admin y regular user?
3. ¿Qué validaciones se aplican en la creación?

Lo que obtienes: Contexto suficiente para un diagnóstico preciso.


Cómo Formular Queries de Investigación Efectivas

Las 5 reglas de una buena query

Regla 1: Sé específico sobre qué quieres saber

❌ Malo:  "Explícame este código"
✅ Bueno: "Explica cómo el endpoint POST /api/tasks valida 
          el input y lo persiste en la base de datos"

Regla 2: Da contexto del por qué investigas

❌ Malo:  "¿Dónde se usa la clase User?"
✅ Bueno: "Voy a agregar un campo 'department' a la clase User. 
          Necesito saber todos los archivos que crean, leen, 
          o modifican usuarios para evaluar el impacto del cambio"

Regla 3: Pide formato accionable

❌ Malo:  "¿Cómo funciona la autenticación?"
✅ Bueno: "Describe el flujo de autenticación paso a paso, 
          indicando archivo y número de línea para cada paso. 
          Incluye qué se valida en cada punto"

Regla 4: Delimita el scope

❌ Malo:  "Analiza todo el proyecto"
✅ Bueno: "Analiza solo los archivos en routers/ y services/ 
          relacionados con la feature de tareas (tasks)"

Regla 5: Pide lo que falta, no lo que ya sabes

❌ Malo:  "Explícame qué hace FastAPI"
✅ Bueno: "Ya sé que el proyecto usa FastAPI con SQLAlchemy.
          Lo que no sé es cómo está configurada la sesión 
          de base de datos y si usa async o sync"

Anatomía de una query de investigación profesional

Estructura óptima:

1. CONTEXTO: Qué sabes y por qué investigas
   "Estoy revisando un bug donde los admins no pueden crear tasks"

2. PREGUNTA ESPECÍFICA: Qué necesitas saber
   "Necesito entender el flujo de creación de tasks y si hay 
    diferencias en el path de código para distintos roles"

3. FORMATO DESEADO: Cómo quieres la respuesta
   "Lista los archivos involucrados con las funciones específicas
    que se llaman, en orden de ejecución"

4. LÍMITES: Qué no necesitas
   "No necesito entender la autenticación — ya la conozco. 
    Solo el flujo posterior al auth"

Ejemplo completo: query bien formulada

Estoy investigando por qué el endpoint PATCH /api/tasks/{id} 
a veces no persiste los cambios en la base de datos. 
El bug es intermitente — funciona la mayoría de las veces.

Necesito entender:
1. El flujo completo del PATCH: desde el request hasta el commit
2. Cómo se maneja la sesión de SQLAlchemy (¿hay commit explícito?)
3. Si hay algún middleware o hook que pueda interferir con la sesión
4. Si la sesión se comparte entre requests o es por-request

Muéstrame los archivos y funciones involucrados con las líneas
relevantes. No necesito entender el flujo de GET ni DELETE — 
solo PATCH.

Esta query va a producir una investigación precisa y enfocada que te ahorra 20-30 minutos de leer código manualmente.


Cuándo Usar Subagents vs Investigación Manual

No toda investigación requiere Claude Code. A veces es más rápido buscar manualmente. La decisión depende del contexto:

Usa subagents de exploración cuando:

✅ El codebase es nuevo para ti
   → No sabes dónde están las cosas
   → Claude Code navega más rápido que tú

✅ Las dependencias cruzan múltiples archivos
   → Un import lleva a otro que lleva a otro
   → Claude Code sigue la cadena automáticamente

✅ Necesitas un mapa completo de un flujo
   → El flujo toca 5+ archivos
   → Claude Code puede mapear todo en una query

✅ No sabes qué buscar
   → "¿Cómo funciona X en este proyecto?"
   → Claude Code explora y te resume

✅ El proyecto tiene convenciones desconocidas
   → ¿Usa repository pattern? ¿Service layer? ¿Algo custom?
   → Claude Code identifica los patrones arquitectónicos

Investiga manualmente cuando:

✅ Sabes exactamente dónde está el problema
   → "El bug está en line 45 de task_service.py"
   → Abre el archivo y lee — más rápido que formular una query

✅ Es un archivo único sin dependencias complejas
   → Una utility function, un helper, un config
   → Leerlo toma 30 segundos

✅ Necesitas ver runtime state
   → Variables, estado de DB, conexiones
   → Claude Code no puede ver esto — usa pdb o logging

✅ El codebase es tuyo y lo conoces bien
   → Ya sabes la arquitectura, ya sabes dónde buscar
   → Grep es más rápido que formular la query

✅ Es una búsqueda textual simple
   → "¿Dónde se usa esta variable?"
   → grep o find-in-files es instantáneo

Tabla de decisión rápida

SituaciónSubagentManual
Codebase nuevo, flujo desconocido✅
Archivo único, bug claro✅
Impacto de un cambio cross-file✅
Buscar string en codebase✅
Entender arquitectura del proyecto✅
Ver valor de variable en runtime✅
Mapear todas las dependencias de un módulo✅
Leer un error message en un log✅

Workflow Completo: Investigar → Entender → Planificar → Cambiar

El flujo de 4 pasos

┌─────────────┐     ┌─────────────┐     ┌──────────────┐     ┌──────────┐
│ INVESTIGAR  │ ──→ │  ENTENDER   │ ──→ │  PLANIFICAR  │ ──→ │ CAMBIAR  │
│             │     │             │     │              │     │          │
│ Subagent:   │     │ "Ahora sé   │     │ "Necesito    │     │ Ejecutas │
│ "¿Cómo      │     │  cómo       │     │  cambiar X   │     │ el plan  │
│  funciona   │     │  funciona   │     │  en archivo  │     │ con      │
│  esto?"     │     │  el         │     │  A, Y en     │     │ precisión│
│             │     │  sistema"   │     │  archivo B"  │     │          │
└─────────────┘     └─────────────┘     └──────────────┘     └──────────┘

Ejemplo práctico: Agregar validación de permisos a un endpoint

Contexto: El endpoint DELETE /api/tasks/{id} permite que cualquier usuario autenticado borre cualquier tarea. Necesitas agregar validación de permisos: solo el owner o un admin puede borrar.

Paso 1: Investigar

Query a Claude Code:

Necesito agregar validación de permisos al endpoint 
DELETE /api/tasks/{id}. Antes de hacer el cambio:

1. ¿Cómo está implementado actualmente el endpoint DELETE?
2. ¿Cómo verifican permisos los otros endpoints (PATCH, etc.)?
3. ¿Hay alguna dependency injection o middleware existente
   para verificación de permisos que pueda reutilizar?
4. ¿El modelo Task tiene un campo owner_id o similar?

Respuesta de Claude Code:

Investigación completada:

1. DELETE /api/tasks/{id} (routers/tasks.py:92)
   - Recibe task_id y current_user (via Depends)
   - Llama task_service.delete_task(task_id)
   - NO verifica ownership ni role
   
2. PATCH /api/tasks/{id} (routers/tasks.py:67)
   - Sí verifica: task.owner_id == current_user["id"]
   - Pero no permite admins — solo el owner puede editar
   
3. Dependencies existentes (dependencies/auth.py):
   - get_current_user() → extrae user del token
   - require_admin() → verifica role == "admin"
   - NO hay una dependency para "owner or admin"
   
4. Modelo Task (models/task.py:12):
   - Tiene owner_id (ForeignKey a users)
   - Relationship: owner = relationship("User")

Paso 2: Entender

Con esta información, ahora entiendes:

  • El endpoint DELETE no tiene validación — cualquier usuario autenticado puede borrar
  • PATCH tiene validación parcial (solo owner, no admin)
  • No existe una dependency reutilizable para "owner or admin"
  • El modelo Task sí tiene owner_id, así que la verificación es posible

Paso 3: Planificar

Plan basado en la investigación:

1. Crear nueva dependency: require_owner_or_admin(task_id, current_user)
   → En dependencies/auth.py
   → Verifica: task.owner_id == current_user["id"] OR role == "admin"

2. Aplicar al DELETE endpoint
   → Agregar Depends(require_owner_or_admin)

3. También actualizar PATCH
   → Reemplazar verificación inline por la nueva dependency
   → Ahora admins también pueden editar

4. Agregar tests para los 3 escenarios:
   → Owner puede borrar ✅
   → Admin puede borrar ✅
   → Otro usuario no puede borrar ❌ (403)

Paso 4: Cambiar

Ahora ejecutas el plan con precisión porque sabes exactamente qué archivos tocar, qué patrón seguir (viste cómo PATCH lo hace), y qué dependency crear.

from fastapi import Depends, HTTPException
from sqlalchemy.orm import Session
from app.database import get_db
from app.models import Task
from app.dependencies.auth import get_current_user


async def require_owner_or_admin(
    task_id: int,
    current_user: dict = Depends(get_current_user),
    db: Session = Depends(get_db),
):
    task = db.query(Task).get(task_id)
    if not task:
        raise HTTPException(status_code=404, detail="Task not found")

    is_owner = task.owner_id == current_user["id"]
    is_admin = current_user.get("role") == "admin"

    if not is_owner and not is_admin:
        raise HTTPException(
            status_code=403,
            detail="Only the task owner or an admin can perform this action",
        )
    return task

Sin la investigación del paso 1, podrías haber:

  • No descubierto que require_admin ya existía (duplicar código)
  • No visto que PATCH tiene una verificación parcial (inconsistencia)
  • No sabido que Task tiene owner_id (buscar el campo equivocado)
  • No visto el patrón de dependencies del proyecto (implementar de forma incompatible)

Errores Comunes al Investigar con Subagents

Error 1: Queries demasiado vagas

❌ "Explícame el proyecto"
   → Respuesta genérica de 2 páginas que no te ayuda

✅ "Explícame cómo un request POST /api/tasks pasa por 
    autenticación, validación, y se persiste en la DB.
    Solo los archivos y funciones involucrados."
   → Respuesta precisa y accionable

Error 2: No dar contexto del problema

❌ "¿Dónde se usa la función get_user?"
   → Lista de 15 usos sin priorización

✅ "Voy a cambiar el tipo de retorno de get_user() de dict a 
    un modelo Pydantic UserResponse. ¿Qué archivos llaman a 
    get_user() y necesitarían actualizarse para manejar el 
    nuevo tipo de retorno?"
   → Lista priorizada de impactos con contexto

Error 3: Pedir investigación cuando deberías actuar

❌ Investigar 10 minutos un off-by-one error que puedes ver 
   en la línea del stack trace
   → Abrir el archivo y arreglar toma 30 segundos

✅ Investigar cuando genuinamente no entiendes el sistema
   → El costo de investigar se paga en decisiones mejores

Error 4: No verificar la investigación

❌ Claude Code dice: "El auth middleware aplica a todos los endpoints"
   Tú: "Ok, confío" → Resulta que /api/health está excluido

✅ Claude Code dice: "El auth middleware aplica a todos los endpoints"
   Tú: Abro middleware.py y confirmo → Descubres 3 paths excluidos

Los subagents de exploración son herramientas de investigación, no oráculos. Verifica hallazgos críticos.

Error 5: Investigar de más (parálisis por análisis)

❌ Pasas 30 minutos investigando cada aspecto del codebase
   antes de hacer un cambio de 3 líneas
   → La investigación debe ser proporcional al cambio

✅ Investigación de 5 minutos para un cambio que toca 3 archivos
   Investigación de 15 minutos para un cambio arquitectónico
   Sin investigación para un typo fix

Ejemplo Avanzado: Investigación Multi-Query

A veces una sola query no es suficiente. Los problemas complejos requieren investigación iterativa:

Escenario: Performance lenta en endpoint de búsqueda

El endpoint GET /api/tasks/search?q=... tarda 3 segundos 
con 10,000 tareas. Necesitas optimizarlo.

Query 1: Entender el endpoint actual

Muéstrame la implementación completa del endpoint 
GET /api/tasks/search. ¿Cómo hace la búsqueda? 
¿Usa SQL directo, ORM queries, o full-text search?

Respuesta de Claude Code:

# routers/tasks.py:34
@router.get("/tasks/search")
async def search_tasks(
    q: str,
    db: Session = Depends(get_db),
    current_user: dict = Depends(get_current_user),
):
    all_tasks = db.query(Task).all()
    results = []
    for task in all_tasks:
        if q.lower() in task.title.lower() or q.lower() in (task.description or "").lower():
            results.append(task)
    return results

Hallazgo: Carga TODAS las tareas en memoria y filtra en Python. Eso explica los 3 segundos.

Query 2: Verificar qué índices existen

¿Qué índices tiene la tabla tasks en la base de datos? 
Revisa el modelo SQLAlchemy y las migraciones de Alembic.
¿Hay algún índice en title o description?

Respuesta:

# models/task.py
class Task(Base):
    __tablename__ = "tasks"
    id = Column(Integer, primary_key=True, index=True)
    title = Column(String(200), nullable=False)  # sin index
    description = Column(Text, nullable=True)      # sin index
    owner_id = Column(Integer, ForeignKey("users.id"), index=True)
    status = Column(String(20), default="pending")
    created_at = Column(DateTime, default=datetime.utcnow)

Hallazgo: No hay índices en title ni description.

Query 3: Evaluar opciones

El proyecto usa PostgreSQL y SQLAlchemy. ¿Hay algún lugar
en el codebase donde se use búsqueda con ILIKE, full-text 
search, o algún mecanismo de búsqueda más eficiente que 
pueda usar como referencia?

Respuesta:

No hay ningún otro endpoint que use búsqueda eficiente. 
Todos los filtros en el proyecto usan .filter() de SQLAlchemy
con comparaciones exactas (==), no búsqueda textual.

Resultado de la investigación multi-query:

Diagnóstico completo:
1. El endpoint carga TODAS las tareas en memoria (O(n) en Python)
2. No hay índices en los campos de búsqueda
3. No hay precedente de búsqueda eficiente en el proyecto

Plan de optimización:
1. Reemplazar búsqueda en Python por ILIKE en SQL
2. Agregar índice en title (y posiblemente GIN para full-text)
3. Agregar paginación (LIMIT/OFFSET)

Sin la investigación multi-query, podrías haber intentado "optimizar" el loop de Python sin darte cuenta de que el problema real es que no debería haber un loop — debería ser una query SQL.


Conexión con Proyecto

En el proyecto integrador (Módulo 8)

Antes de tocar una línea del codebase del proyecto, tu primera tarea será investigar:

  1. Estructura del proyecto — ¿Cómo está organizado? ¿Qué patrones usa?
  2. Flujos principales — ¿Cómo funcionan los endpoints principales?
  3. Dependencias — ¿Qué depende de qué?
  4. Áreas problemáticas — ¿Dónde están los code smells, hallucinations, security holes?

La documentación de tu investigación es parte de la entrega. No basta con "encontré 15 problemas" — necesitas "investigué el codebase, entendí la arquitectura, y basado en eso identifiqué 15 problemas priorizados."

Cómo la investigación ahorra tiempo en el proyecto

Sin investigación previa:
├── Arreglas un bug → rompes una dependencia que no conocías
├── Renombras una función → 3 archivos que la usan se rompen
├── Cambias un schema → el endpoint que lo usa sigue usando el viejo
└── Total: 3 horas con muchos ida-y-vuelta

Con investigación previa (15-20 minutos):
├── Tienes un mapa de dependencias
├── Sabes qué archivos toca cada cambio
├── Priorizas los cambios por impacto
└── Total: 1.5 horas con cambios limpios

Troubleshooting

Problema 1: "Las respuestas de investigación de Claude Code son demasiado genéricas"

Causa: La query es demasiado amplia o no tiene contexto suficiente. Solución: Aplica las 5 reglas de queries efectivas. En particular, sé específico sobre qué flujo o componente te interesa, da contexto de por qué investigas (qué problema estás resolviendo), y pide formato accionable (archivos con líneas, no prosa genérica).

Problema 2: "No sé qué preguntar — no conozco el codebase"

Causa: Es normal cuando enfrentas un codebase nuevo. No sabes qué no sabes. Solución: Empieza con queries estructurales: "¿Cómo está organizado este proyecto? ¿Qué hay en cada directorio? ¿Cuál es la entry point?" Eso te da el mapa base. Después profundiza en los flujos específicos que necesitas entender.

Problema 3: "Claude Code me da información incorrecta sobre el codebase"

Causa: Puede ocurrir, especialmente con codebase grandes o complejos. Solución: Siempre verifica hallazgos críticos. Si Claude Code dice "la función X no se usa en ningún otro archivo," abre la terminal y haz grep -r "función_X" para confirmar. La investigación con subagents es punto de partida, no verdad absoluta.

Problema 4: "La investigación tarda más que simplemente leer el código yo mismo"

Causa: Para codebases pequeños o archivos individuales, la investigación manual es más rápida. Solución: Usa la tabla de decisión: subagents para flujos cross-file, codebase desconocidos, y mapeo de dependencias. Investigación manual para archivos individuales, bugs puntuales, y codebases que ya conoces.

Problema 5: "Investigué mucho pero sigo sin saber qué hacer"

Causa: Parálisis por análisis o la investigación no está enfocada en el problema correcto. Solución: Reformula tu problema. Si después de 3 queries no tienes claridad, es probable que estés investigando el aspecto equivocado. Pregunta algo diferente o prueba reproducir el problema manualmente para tener datos concretos que guíen la investigación.


Ejercicios

Ejercicio 1: Formular queries de investigación (Medio)

Para cada escenario, escribe una query de investigación que seguiría las 5 reglas de queries efectivas:

Escenario A: Necesitas agregar paginación al endpoint GET /api/tasks pero no sabes cómo el proyecto maneja responses actualmente.

Escenario B: Un bug: POST /api/tasks crea tareas duplicadas intermitentemente. No sabes por qué.

Escenario C: Te piden migrar de SQLite a PostgreSQL. Necesitas saber cuántos archivos se afectan.

Escenario D: El endpoint GET /api/tasks/{id} devuelve datos de relaciones (tags, categories) pero no sabes cómo están configurados los relationships en SQLAlchemy.

Ver solución

Escenario A:

Necesito agregar paginación al endpoint GET /api/tasks.
Antes de implementar:

1. ¿Cómo devuelve actualmente la lista de tasks? 
   ¿Devuelve todas o tiene algún límite?
2. ¿Hay algún otro endpoint en el proyecto que ya 
   implemente paginación que pueda usar como referencia?
3. ¿El response usa un schema Pydantic o devuelve 
   los modelos SQLAlchemy directamente?
4. ¿Hay tests existentes para este endpoint que 
   necesite actualizar?

Solo necesito entender el patrón de response, 
no el flujo de autenticación ni validación.

Escenario B:

Bug: POST /api/tasks crea tareas duplicadas de forma 
intermitente (no siempre, solo a veces). Antes de 
diagnosticar:

1. ¿Cuál es el flujo completo de creación? Desde el 
   request hasta el commit en DB.
2. ¿Hay algún middleware, hook, o event listener que 
   se ejecute durante la creación de tasks?
3. ¿El endpoint tiene retry logic o algún mecanismo 
   que pueda causar doble ejecución?
4. ¿Cómo se maneja la sesión de DB? ¿Podría haber 
   un commit doble?

El hecho de que sea intermitente sugiere un timing 
issue — busco puntos donde la ejecución podría 
duplicarse.

Escenario C:

Voy a migrar la base de datos de SQLite a PostgreSQL.
Necesito evaluar el impacto:

1. ¿Dónde se configura la conexión a la DB? 
   ¿Cuántos archivos la referencian?
2. ¿Hay queries con syntax específica de SQLite 
   que no sea compatible con PostgreSQL?
3. ¿Las migraciones de Alembic son database-agnostic 
   o tienen operaciones específicas de SQLite?
4. ¿Hay algún uso de características exclusivas 
   de SQLite (como autoincrement behavior)?
5. ¿Los tests usan una DB separada o la misma 
   configuración de producción?

Lista todos los archivos que tendría que modificar 
con el tipo de cambio necesario en cada uno.

Escenario D:

El endpoint GET /api/tasks/{id} devuelve datos de 
relaciones (tags, categories) y necesito entender 
cómo están configurados:

1. ¿Cómo está definido el modelo Task? ¿Qué 
   relationships tiene?
2. ¿Las relaciones usan lazy loading, eager loading, 
   o selectin loading?
3. ¿El schema de response (Pydantic) incluye los 
   nested models o solo IDs?
4. ¿Hay tablas de asociación (many-to-many) o 
   son relaciones directas (one-to-many)?

Solo necesito entender las relaciones del modelo Task,
no los demás modelos.

Ejercicio 2: Investigación vs acción directa (Fácil)

Para cada situación, decide si deberías investigar con subagents primero o actuar directamente. Justifica tu decisión.

  1. Tienes un TypeError: 'NoneType' has no attribute 'id' en la línea 45 de task_service.py
  2. Te piden agregar un sistema de notificaciones por email al proyecto
  3. Un test falla con AssertionError: expected 200, got 422
  4. Necesitas entender por qué el proyecto tiene dos archivos de configuración: config.py y settings.py
  5. Hay un f-string en un SQL query en user_repository.py:23
Ver solución
  1. Actuar directamente. El stack trace te dice exactamente dónde está el problema. Abre task_service.py:45 y verifica qué puede ser None. Investigar sería overkill.

  2. Investigar primero. Un sistema de notificaciones toca múltiples archivos (modelos, servicios, configuración, endpoints). Necesitas entender la arquitectura actual y dónde integrar el nuevo feature sin romper lo existente.

  3. Actuar directamente. Un 422 significa validación fallida. Revisa el test para ver qué datos envía y el endpoint para ver qué validación aplica. Es un problema localizado.

  4. Investigar. Esto es una pregunta arquitectónica. Necesitas que Claude Code te explique qué contiene cada archivo, si se complementan o si uno es legacy, y cuál se usa realmente.

  5. Actuar directamente. Esto es un security hole conocido (SQL injection). No necesitas investigar — necesitas reemplazar el f-string por un parameterized query. Lo aprendiste en el módulo 5.

Ejercicio 3: Investigación multi-query (Difícil)

Tienes esta situación: un endpoint GET /api/reports/summary tarda 8 segundos en responder. No sabes nada del codebase. Escribe una secuencia de 3 queries de investigación, donde cada query se basa en lo que esperarías aprender de la anterior.

Ver solución

Query 1: Entender el endpoint

Muéstrame la implementación completa del endpoint 
GET /api/reports/summary. ¿Qué datos calcula? ¿Qué 
tablas consulta? ¿Cuántas queries ejecuta?

Hipótesis después de Query 1: Probablemente hace múltiples queries o carga muchos datos.

Query 2: Analizar las queries

[Basado en lo que aprendí] El endpoint llama a 
ReportService.generate_summary() que ejecuta 5 queries 
separadas. Para cada query:
1. ¿Hay índices en las columnas que filtra?
2. ¿Alguna query carga relaciones con lazy loading 
   que podrían causar N+1?
3. ¿Se podría combinar alguna de las 5 queries 
   en una sola?

Hipótesis después de Query 2: Identificas cuáles queries son el cuello de botella.

Query 3: Verificar patrones existentes

[Basado en lo que aprendí] Las queries 2 y 3 causan 
N+1 loading y no hay índices en created_at. 
¿Hay algún lugar en el proyecto donde se use:
1. joinedload o selectinload para evitar N+1?
2. Caching (Redis, in-memory) para datos que no 
   cambian frecuentemente?
3. Índices compuestos o parciales?

Quiero saber si hay precedentes que pueda seguir 
para mantener consistencia.

Resultado: Después de 3 queries, tienes un diagnóstico completo del problema de performance y un plan basado en los patrones existentes del proyecto.

Ejercicio 4: Evaluar calidad de una investigación (Medio)

Claude Code te devuelve esta investigación. Identifica qué está bien, qué falta, y qué verificarías manualmente:

Investigación: Flujo de creación de tareas

1. POST /api/tasks → routers/tasks.py:create_task()
2. Valida con TaskCreate schema (schemas/task.py)
3. Llama task_service.create(task_data, user)
4. TaskService crea el modelo y hace db.add() + db.commit()
5. Devuelve TaskResponse con el task creado

La función create_task no tiene middleware especial.
No hay validación de duplicados.
El user se extrae del token JWT.
Ver solución

Lo que está bien:

  • ✅ Identifica los archivos y funciones correctamente
  • ✅ Muestra el flujo secuencial paso a paso
  • ✅ Menciona la ausencia de validación de duplicados (útil si investigas duplicados)

Lo que falta:

  • ❌ No muestra los números de línea — hace más difícil verificar
  • ❌ No menciona error handling — ¿qué pasa si el commit falla?
  • ❌ No menciona si hay events o signals que se disparan post-create
  • ❌ No dice qué campos tiene TaskCreate — necesitas esto para entender la validación
  • ❌ No menciona la sesión de DB — ¿se comparte? ¿se cierra después del commit?

Lo que verificarías manualmente:

  1. Abrir routers/tasks.py y confirmar que create_task() es en efecto la función del endpoint POST
  2. Verificar schemas/task.py para ver los campos de TaskCreate (el subagent podría haber omitido validaciones importantes)
  3. Confirmar que no hay middleware que intercepte POST requests (el subagent dice "no hay middleware especial" — ¿verificó todos los middleware registrados?)
  4. Revisar si hay tests existentes que muestren el comportamiento esperado

Ejercicio 5: Planificar un cambio basado en investigación (Difícil)

Después de investigar, obtuviste esta información:

Codebase: API de gestión de inventario

Archivos relevantes:
- models/product.py     → Modelo Product(id, name, price, stock, category_id)
- models/category.py    → Modelo Category(id, name, description)
- schemas/product.py    → ProductCreate, ProductUpdate, ProductResponse
- routers/products.py   → CRUD endpoints para productos
- services/product_service.py → Lógica de negocio
- dependencies/auth.py  → get_current_user, require_admin

Hallazgos:
1. ProductResponse no incluye el nombre de la categoría (solo category_id)
2. El endpoint GET /products no permite filtrar por categoría
3. No hay validación de que category_id exista al crear producto
4. El endpoint DELETE no verifica permisos (cualquier user puede borrar)

Escribe un plan de cambios priorizado, indicando para cada cambio: qué archivos toca, el orden de ejecución, y si es un fix de seguridad, funcionalidad, o UX.

Ver solución

Plan priorizado:

Prioridad 1 (SEGURIDAD):
─────────────────────────
4. Agregar verificación de permisos a DELETE
   Archivos: routers/products.py, dependencies/auth.py
   Cambio: Agregar Depends(require_admin) al endpoint DELETE
   Tipo: Security fix
   Razón: Cualquier usuario puede borrar productos — 
          vulnerabilidad crítica

Prioridad 2 (INTEGRIDAD DE DATOS):
──────────────────────────────────
3. Validar que category_id exista al crear producto
   Archivos: services/product_service.py
   Cambio: Verificar que la categoría existe antes de crear
   Tipo: Data integrity fix
   Razón: Puede crear productos con categorías inexistentes

Prioridad 3 (FUNCIONALIDAD):
────────────────────────────
2. Agregar filtro por categoría a GET /products
   Archivos: routers/products.py, services/product_service.py
   Cambio: Agregar query parameter category_id opcional
   Tipo: Feature
   Razón: Funcionalidad esperada que falta

Prioridad 4 (UX):
─────────────────
1. Incluir nombre de categoría en ProductResponse
   Archivos: schemas/product.py, routers/products.py
   Cambio: Agregar category_name a ProductResponse, 
           hacer join en la query
   Tipo: UX improvement
   Razón: El frontend necesita mostrar el nombre, 
          no solo el ID

Orden de ejecución: 4 → 3 → 2 → 1
(seguridad primero, UX al final)

Resumen

  • Investigar antes de actuar es el patrón que separa a los developers efectivos de los que luchan con AI tools
  • Las capacidades de exploración de Claude Code permiten navegar archivos, seguir imports, mapear dependencias, y entender flujos — todo antes de cambiar una línea
  • Hay 4 tipos de investigación: entender flujos, encontrar usos, mapear impacto, y investigar bugs
  • Las 5 reglas de queries efectivas: sé específico, da contexto, pide formato accionable, delimita scope, pide lo que falta
  • Usa subagents para codebase desconocidos, flujos cross-file, y mapeo de dependencias
  • Usa investigación manual para archivos individuales, bugs puntuales, y codebases conocidos
  • El workflow completo es: investigar → entender → planificar → cambiar
  • Verifica hallazgos críticos — los subagents son herramientas de investigación, no oráculos
  • La investigación con subagents es el prerequisito para tomar buenas decisiones de regenerar vs editar (cápsulas 03-05)

Recursos Adicionales

  1. Anthropic — Claude Code Best Practices - Documentación oficial de Claude Code incluyendo capacidades de exploración
  2. Architecture Decision Records - Cómo documentar decisiones arquitectónicas basadas en investigación
  3. Code Reading: The Open Source Perspective - Técnicas de lectura y comprensión de código
  4. Working Effectively with Legacy Code - Michael Feathers — investigar codebases existentes antes de hacer cambios
  5. The Pragmatic Programmer — Tracer Bullets - Técnica de investigar y prototipar antes de construir

Siguiente cápsula: Cuándo Regenerar Código — las señales claras de que regenerar es mejor que editar.


Debugging & Code Review with Claude Code — Módulo 7, Cápsula 02 Claude Code Agentic Development Path — Guía #6 de 11