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ón | Subagent | Manual |
|---|---|---|
| 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_adminya 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:
- Estructura del proyecto — ¿Cómo está organizado? ¿Qué patrones usa?
- Flujos principales — ¿Cómo funcionan los endpoints principales?
- Dependencias — ¿Qué depende de qué?
- Á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.
- Tienes un
TypeError: 'NoneType' has no attribute 'id'en la línea 45 detask_service.py - Te piden agregar un sistema de notificaciones por email al proyecto
- Un test falla con
AssertionError: expected 200, got 422 - Necesitas entender por qué el proyecto tiene dos archivos de configuración:
config.pyysettings.py - Hay un f-string en un SQL query en
user_repository.py:23
Ver solución
-
Actuar directamente. El stack trace te dice exactamente dónde está el problema. Abre
task_service.py:45y verifica qué puede ser None. Investigar sería overkill. -
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.
-
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.
-
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.
-
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:
- Abrir
routers/tasks.pyy confirmar quecreate_task()es en efecto la función del endpoint POST - Verificar
schemas/task.pypara ver los campos deTaskCreate(el subagent podría haber omitido validaciones importantes) - Confirmar que no hay middleware que intercepte POST requests (el subagent dice "no hay middleware especial" — ¿verificó todos los middleware registrados?)
- 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
- Anthropic — Claude Code Best Practices - Documentación oficial de Claude Code incluyendo capacidades de exploración
- Architecture Decision Records - Cómo documentar decisiones arquitectónicas basadas en investigación
- Code Reading: The Open Source Perspective - Técnicas de lectura y comprensión de código
- Working Effectively with Legacy Code - Michael Feathers — investigar codebases existentes antes de hacer cambios
- 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