El test lleva dos días en rojo en CI. En local pasa. Le pegas el error a Claude Code, escribes "arréglalo" y en cuarenta segundos tienes un check verde.
Abres el diff. La aserción que esperaba "12/10/2026" ahora espera "11/10/2026". O el cuerpo de la función está envuelto en un try/catch que devuelve una cadena vacía. El bug sigue ahí.
El agente no ha mentido: le pediste un check verde y te lo dio. Depurar con Claude Code sin pedir una explicación es pedirle que adivine.
En corto: para depurar con Claude Code, no pidas el fix: pide que reproduzca el fallo, que formule hipótesis y que las descarte con evidencia (logs, un test que falla). Solo con una hipótesis confirmada se escribe el fix, junto a un test de regresión. Y cierra las salidas fáciles: prohíbe tocar aserciones y revisa el diff de los tests antes que el del código.
¿Qué es depurar con hipótesis?
Depurar con hipótesis es tratar cada causa posible de un bug como una afirmación que hay que demostrar o descartar con una prueba observable antes de cambiar una sola línea de código.
El método es el de siempre. Lo que cambia con un agente es el coste de saltárselo: Claude Code escribe un parche en segundos, y un agente que prueba a ciegas acaba encontrando el parche que pone el check en verde, que casi nunca es el que arregla la causa.
La guía de buenas prácticas de Anthropic lo recoge en su tabla de ejemplos: el prompt bueno para un build roto termina con "address the root cause, don't suppress the error" (best practices de Claude Code, consultadas en octubre de 2026). Si no lo pides, nada garantiza que lo haga.
Por qué, al depurar con Claude Code, te parchea el síntoma
Claude Code se detiene cuando el trabajo parece terminado. La documentación oficial lo dice: sin un check que pueda ejecutar, "looks done" es la única señal que tiene.
Si tu único check es "el test pasa", hay tres formas de cumplirlo: arreglar el código, cambiar lo que el test espera o hacer que el test deje de comprobar nada. Las dos últimas son más cortas.
No es un caso aislado. En Hacker News, el usuario skerit contó en marzo de 2025 que añadía a cada petición reglas como no cambiar el test "by removing assertions, adding empty try/catch blocks, or making tests trivial", y que aun así no lo resolvía al 100% (comentario en HN). En el repo oficial está el issue #7074, "[BUG] Claude Code manipulates tests instead of following instructions", abierto en septiembre de 2025 y cerrado como duplicado.
Estos son los parches de síntoma más repetidos y cómo cazarlos en el diff:
| Parche de síntoma | Cómo se ve en el diff | Riesgo en producción |
|---|---|---|
| Cambiar el valor esperado | Cambia la línea expect(...), el código no |
El bug se queda y el test ahora lo protege |
| Borrar o saltar el test | it.skip, xit, test comentado o eliminado |
Pierdes la red justo donde fallaba |
try/catch que se traga el error |
catch {} nuevo o un return por defecto |
El fallo se vuelve silencioso y corrompe datos |
| Condicional para el caso del test | if (process.env.CI) o un valor mágico |
El código se comporta distinto en test y en producción |
| Fix real | Cambia el código, hay un test nuevo y las aserciones viejas siguen igual | Ninguno propio; puede haber otras causas sin cubrir |
Los fallos generales de adopción ya los repasé en errores comunes al adoptar Claude Code. Aquí vamos al flujo.
El flujo para depurar con Claude Code en cuatro pasos
La regla: el agente no edita código de producción hasta que una hipótesis esté confirmada con evidencia. Para forzarla, empieza en plan mode.
Plan mode hace que Claude lea y proponga sin editar nada hasta que apruebes. Se activa con Shift+Tab hasta que la barra muestra ⏸ plan mode on, o con claude --permission-mode plan (documentación oficial). Lo explico a fondo en plan mode en Claude Code.
1. Reproducir el fallo
Sin reproducción no hay depuración, hay especulación. El primer prompt no pide arreglar nada:
El test de formatInvoiceDate falla en CI y pasa en local. Error:
[pega el error y el stack trace]
No edites ningún archivo. Dame el comando exacto que reproduce el fallo
en local y ejecútalo. Si no lo reproduces, lista qué diferencias de
entorno entre CI y local podrían explicarlo.
La guía de flujos comunes de Anthropic recomienda lo mismo: dar el comando que reproduce el problema y decir si el fallo es intermitente o constante.
2. Formular hipótesis
Con el fallo reproducido, pide causas, no soluciones. Y cada una con la prueba que la descartaría:
Se reproduce con TZ=UTC npm test -- formatInvoiceDate (CI corre en UTC;
mi máquina, en Europe/Madrid).
Dame entre 2 y 4 hipótesis de causa raíz. Para cada una: qué evidencia
la confirmaría o la descartaría y cómo obtenerla sin cambiar el
comportamiento del código. Ordénalas por probabilidad.
Todavía no propongas el fix.
Ese "todavía no" importa. Si mezcla hipótesis y fix en la misma respuesta, el fix gana y las hipótesis son decoración.
3. Comprobar cada hipótesis con evidencia
Aquí sales de plan mode, porque comprobar exige escribir: logs temporales y un test nuevo. Pero con un permiso estrecho:
Comprueba las hipótesis en orden. Puedes añadir logs temporales y
escribir un test nuevo que falle. No toques código de producción ni
aserciones de tests existentes. Para cada hipótesis: CONFIRMADA o
DESCARTADA, con la salida exacta del comando que lo demuestra.
Pedir la salida literal es lo que más rinde. La guía oficial insiste en que Claude enseñe evidencia en lugar de afirmar que funciona, porque revisar evidencia es más rápido que repetir tú la verificación.
El entregable es un test que falla por la causa correcta:
it('formats the invoice date in the invoice timezone, not the runtime one', () => {
const issuedAt = new Date('2026-10-11T23:30:00Z');
expect(formatInvoiceDate(issuedAt, 'Europe/Madrid')).toBe('12/10/2026');
});
formatInvoiceDate ya recibía la zona de la factura; el bug es que la ignoraba y formateaba con la del proceso. Si este test no falla antes del fix, no has confirmado nada.
4. El fix y el test de regresión
Ahora sí:
La hipótesis 1 está confirmada. Implementa el fix mínimo en
formatInvoiceDate. El test nuevo debe pasar; los existentes no se
modifican; nada de try/catch que oculte el error ni condicionales por
entorno. Ejecuta la suite y pega la salida. Después explícame en dos
frases por qué el fix ataca la causa y no el síntoma.
Esa explicación final es tu detector: si no sabe explicar la causa en dos frases, desconfía del fix. Es el mismo orden que sigo en Construye con IA: primero la evidencia de qué tiene que pasar, después el código.
Cómo impedir que Claude Code toque las aserciones
Los prompts son peticiones. La documentación de Claude Code distingue entre CLAUDE.md, que es orientativo, y los hooks, que son deterministas. Tienes tres capas.
Revisa primero el diff de los tests. Antes de leer el fix, ejecuta git diff HEAD -- '*.test.ts' '*.spec.ts'. Si una aserción existente cambió, el fix queda rechazado.
Pide una revisión en contexto limpio. La guía recomienda un subagente que revise el diff en un contexto nuevo: no ve el razonamiento que produjo el cambio y lo juzga por sí mismo. Basta con "usa un subagente para revisar este diff y dime si algún test perdió aserciones".
Bloquea la edición con un hook. Un hook PreToolUse con matcher Edit|Write recibe por stdin un JSON con tool_input.file_path, y si sale con código 2 bloquea la llamada (referencia de hooks). Lo monto paso a paso en hooks de Claude Code como guardrails, y en el mod que no deja tocar tus tests cierro también la puerta de la terminal.
Si la sesión se tuerce, no insistas. Esc para a Claude a mitad de acción, /rewind (o Esc dos veces) restaura conversación y código, y la guía oficial aconseja /clear y un prompt mejor si ya has corregido a Claude más de dos veces sobre lo mismo.
Límites de este flujo
Es lento para bugs triviales. Si el fix cabe en una frase, cuatro pasos sobran. La guía de buenas prácticas lo dice de plan mode: si puedes describir el diff en una frase, sáltate el plan.
No sirve si no puedes reproducir. Fallos que solo aparecen con carga real, condiciones de carrera o datos que no tienes en local no salen con un comando. Ahí el paso 1 es añadir observabilidad y esperar.
Las hipótesis salen del contexto que le das. Si la causa está en un servicio externo, en la infraestructura o en un cambio fuera del repo, el agente no la propondrá. Añade tú las que conoces.
Los checkpoints no son git. /rewind solo deshace cambios hechos con las herramientas de edición de Claude, no los hechos desde Bash (checkpointing). Haz commit antes de empezar.
Cómo aplicar este flujo en tu próximo bug
Guarda los cuatro prompts en un archivo del repo y úsalos la próxima vez que vayas a depurar con Claude Code un test en rojo. Mide una sola cosa: cuántos fixes tocan una aserción existente. Si es más de cero, sube una capa.
La tesis es la misma que la de Spec-Driven Development: el agente rinde cuando sabe qué significa "correcto" antes de escribir código. Al depurar, "correcto" es una hipótesis confirmada y un test que falla por la razón adecuada.
Preguntas frecuentes
¿Hay que usar plan mode siempre para depurar con Claude Code?
No. Sirve para reproducir y formular hipótesis sin editar. Para comprobarlas tienes que salir, porque plan mode puede ejecutar comandos pero no escribir el test ni los logs. En bugs triviales puedes saltártelo.
¿Basta con escribir "no modifiques los tests" en CLAUDE.md?
Ayuda, pero no garantiza nada. CLAUDE.md es orientativo; un hook se ejecuta siempre. Si un test es crítico, protégelo con un hook o revisa su diff antes de aceptar cualquier fix.
¿Qué hago si Claude Code no consigue reproducir el fallo?
No le dejes pasar al fix. Pídele las diferencias de entorno entre donde falla y donde no (zona horaria, versiones, variables, datos) y cómo capturar más información. Un fix sin reproducción es una apuesta.
¿Cuántas hipótesis debo pedirle?
Entre dos y cuatro, ordenadas por probabilidad y con la evidencia que confirmaría cada una. Con una sola, el agente se ata a la primera idea. Con diez, gastas contexto en causas improbables.
¿El test de regresión lo escribe Claude Code o yo?
Puede escribirlo Claude Code, pero antes del fix, y tienes que verlo fallar. Un test que nunca estuvo en rojo no demuestra que proteja contra el bug.
Por Bezael Pérez — Developer senior con más de 15 años de experiencia y fundador de Dominicode.

Leave a Reply