🤔 Por qué hacer TDD con un agente
El agente es un pésimo verificador de su propio trabajo. Lee el código que acaba de generar, ve que "parece correcto", y declara la victoria. Sin una prueba en ejecución, «parece correcto» es solo una opinión, y la opinión del agente está sesgada por el código que escribió.
El TDD lo resuelve cambiando las reglas: primero viene la prueba, primero falla y solo después se escribe el código para que la prueba pase. El agente no puede engañarse: o la prueba pasa, o no pasa.
🎯 El principio central
Los agentes necesitan señales objetivas de progreso. Sin esto, dan vueltas en círculo, «arreglando» cosas que ya funcionaban y rompiendo lo que estaba bien.
- • Prueba roja = queda trabajo por hacer
- • Prueba verde = puedes parar
- • Sin pruebas = sin criterio para detenerse
💡 Consejo práctico
Antes de pedirle al agente que implemente algo nuevo, pregunta: "¿cuál es la prueba que demuestra que esto funciona?". Si no puedes describir la prueba, el agente tampoco podrá verificar el resultado.
🔄 /tdd: el ciclo red-green-refactor
El comando /tdd pone al agente en un ciclo disciplinado de tres etapas. Cada etapa tiene un único objetivo: no saltar, no combinar ni optimizar antes de tiempo.
Red: escribe una prueba que falla
Tiempo: 2-5 min
Define el comportamiento esperado en forma de prueba. Ejecuta la prueba y comprueba que falle. Si pasa de inmediato, la prueba está mal o la funcionalidad ya existe. Verla fallar demuestra que la prueba mide algo real.
Green: código mínimo para pasar
Tiempo: 5-15 min
Escribe la menor cantidad de código posible para que la prueba pase. Se permite hardcode. Se permite que quede feo. Se permite que sea lento. El objetivo es solo ver el verde — mejorará en la próxima etapa.
Refactor: limpia sin dejar de estar en verde
Tiempo: 5-10 min
Ahora sí: elimina duplicaciones, pon mejores nombres, extrae funciones. Después de cada cambio, ejecuta las pruebas. Si algo falla, deshazlo. La prueba es la red de seguridad que hace que el refactor sea seguro.
💻 Diálogo real de /tdd
# você /tdd implementa função slugify(texto) — minúsculas, espaço vira "-", remove acentos, remove caracteres especiais # agente 🔴 Red: escrevi 4 testes em test_slugify.py - "Olá Mundo" → "ola-mundo" - " Espaço extra " → "espaco-extra" - "Já!!" → "ja" - "" → "" rodando pytest... 4 failed (módulo slugify não existe) ✓ esperado 🟢 Green: criando slugify.py com unicodedata + re.sub rodando pytest... 4 passed ✓ ♻️ Refactor: extraindo etapas em funções nomeadas _strip_accents(s), _to_kebab(s) rodando pytest após cada mudança... 4 passed ✓ pronto.
🍰 Vertical slices: una parte completa a la vez
Cuando una feature afecta varias capas (UI, API, DB), hay dos formas de abordarla: horizontalmente (hace toda la DB, después toda la API, después toda la UI) o verticalmente (una feature de principio a fin, luego la siguiente). Con un agente, siempre gana el enfoque vertical.
Vertical slice = puedes ejecutarla y ver que funciona al final de cada slice. Horizontal = solo descubres que todo está mal cuando lo juntas todo al final.
✓ Vertical (HACER)
- ✓ Funcionalidad «crear comentario»: migración + endpoint + formulario, todo en una slice
- ✓ La prueba end-to-end se ejecuta al final de cada slice
- ✓ Demostrable: se puede mostrar funcionando después de cada slice
- ✓ Comentarios rápidos: integración probada con cada funcionalidad
✗ Horizontal (EVITAR)
- ✗ "Primero hago todas las tablas y después todos los endpoints"
- ✗ Nada se ejecuta de principio a fin hasta terminar el proyecto
- ✗ Integración big bang: todo se rompe junto al final
- ✗ El agente pierde contexto entre capas y se vuelve inconsistente
📐 Vertical vs. Horizontal — diagrama
VERTICAL (recomendado) HORIZONTAL (evitar) ┌──────┬──────┬──────┐ ┌──────────────────┐ │ UI │ UI │ UI │ │ UI │ ← slice 3 ├──────┼──────┼──────┤ ├──────────────────┤ │ API │ API │ API │ │ API │ ← slice 2 ├──────┼──────┼──────┤ ├──────────────────┤ │ DB │ DB │ DB │ │ DB │ ← slice 1 └──────┴──────┴──────┘ └──────────────────┘ feat1 feat2 feat3 tudo de uma vez cada slice roda só roda no fim de tudo end-to-end (e geralmente quebra)
💡 Regla práctica
Si el agente termina un slice y no puedes abrir la app y usar esa funcionalidad, el slice no está terminado. Vertical = se puede mostrar al final.
🔬 /diagnose: para bugs difíciles
El TDD resuelve bien el trabajo nuevo. Pero los bugs en código existente —especialmente los intermitentes, los que solo ocurren en producción, los que nadie entiende— requieren otro enfoque. El /diagnose es un ciclo de seis etapas pensado para eso.
Reproduce
Consigue ver el error en acción, de forma confiable y en el escenario más pequeño posible. Sin reproducirlo, cualquier «fix» es una suposición.
Minimiza
Reduce el caso hasta dejarlo en lo esencial: menos archivos, menos datos, menos pasos. Cuanto más pequeño sea el caso, más evidente será la causa.
Hypothesise
Enumera las hipótesis posibles. No vayas directamente a la primera que parece buena: escribe 3-5 y ordénalas por probabilidad.
Instrument
Agrega logs, prints, breakpoints que demuestren o refuten cada hipótesis. No lo corrijas todavía: solo mídelo.
Corrección
Ahora sí, con la causa confirmada, escribe la solución. Mínima, enfocada, sin aprovechar para «también arreglar esto de aquí».
Regression-test
Convierte la reproducción del paso 1 en una prueba automatizada. Si el bug vuelve algún día, el CI avisa antes que el usuario.
📊 Por qué este ciclo en lugar de «ir arreglando»
Un bug difícil es difícil porque tu intuición ya falló al menos una vez (si no, ya lo habrías corregido). Saltar a «fix» antes de «reproduce + instrument» es apostar a que esta vez tu intuición acertará; por lo general, no acierta, y el agente refuerza el error escribiendo código que parece resolverlo, pero no lo hace.
🎯 Reproducir antes de corregir
El error más costoso al depurar es saltarse el paso de reproducción. El agente lee el stack trace, ve un nombre familiar y se pone a escribir un fix. A veces funciona, pero cuando no funciona, nadie se da cuenta porque nadie preparó el escenario para verificarlo.
✓ Reproducir primero
- ✓ "Antes de tocar el código, voy a escribir una prueba mínima que falle igual que el bug"
- ✓ Confirmar que la prueba falla por el motivo correcto (mismo error, mismo stack)
- ✓ Solo entonces aborda la causa y mide el resultado con la misma prueba
- ✓ Cuando la prueba pasa, tienes PRUEBAS de que lo corregiste
✗ Antipatrón: saltar directo a la solución
- ✗ "Ah, ya sé qué es, lo voy a corregir" — sin reproducirlo
- ✗ Agente que lee el error y enseguida empieza a modificar 5 archivos
- ✗ "Creo que era eso" — sin forma de demostrarlo
- ✗ El bug vuelve una semana después porque el «fix» no corrige el caso real
⚠️ Señal de alerta
Si el agente dice "voy a ajustar esto aquí" y empieza a editar código sin haber mostrado una reproducción, interrúmpelo. Primero pídele: "¿cómo reproduces el bug?". Si no sabe responder, está adivinando.
🛡️ Pruebas de regresión: cada bug corregido se convierte en una prueba
Un bug que volvió es el más frustrante de todos, porque ya se había corregido una vez. La regla es sencilla: cada bug corregido se convierte en una prueba de regresión. Si no tienes una prueba para el caso, volverá a ocurrir.
El mejor momento para escribir esta prueba es justo después de corregir el bug: el escenario está fresco, sabes exactamente qué fallaba y la reproducción ya existe (la usaste en /diagnose).
💻 Ejemplo: prueba de regresión
# Bug reportado em produção: "comentário com emoji 🎉 quebrava a API" # Causa encontrada via /diagnose: encoding latin-1 num middleware antigo # Fix aplicado: trocar pra utf-8 # Teste de regressão (commit junto com o fix): def test_comentario_com_emoji_nao_quebra_api(): """Regression: bug #482 — emoji no body causava UnicodeDecodeError.""" payload = {"texto": "Show de bola 🎉🚀"} response = client.post("/api/comentarios", json=payload) assert response.status_code == 201 assert response.json()["texto"] == "Show de bola 🎉🚀" # Anote no docstring: número do bug, causa raiz. Quando o teste falhar # de novo em 6 meses, quem ler vai entender o porquê de existir.
💡 Si no tiene una prueba, el bug vuelve
No es pesimismo, es estadística. Refactors futuros, cambios en dependencias, alguien limpiando «código viejo que nadie usa»: cualquiera de estas cosas puede hacer que el bug vuelva. La prueba es el recordatorio automático: "este caso debe seguir funcionando".
📊 Patrón de docstring
- • Prefijo:
Regression:deja explícito que la prueba existe por un bug - • Referencia: número del issue/ticket/PR para rastrear el contexto
- • Causa raíz: una línea — para que quien modifique la prueba después lo entienda
- • NO borrar: la prueba de regresión es patrimonio del proyecto
🔀 Combinando /tdd + /diagnose
Los dos loops no compiten — colaboran. /tdd es para trabajo nuevo cuando sabes lo que quieres. /diagnose es para entender por qué algo existente no está haciendo lo que debería. Los bugs complejos casi siempre pasan por ambos.
🎬 Flujo: error intermitente en producción
Imagina: informe de bug "a veces el pago se duplica". No puedes reproducirlo en local. ¿Qué haces?
- 1. /diagnose — reproduce: no puede. Retrocede un paso: instrumenta producción (logs de transaction id, timestamp, request id).
- 2. /diagnose — plantea una hipótesis: ¿retry del cliente? ¿race condition en el worker? ¿webhook duplicado del gateway?
- 3. /diagnose — instrument + minimise: los registros muestran que dos workers toman el mismo job cuando el cliente reintenta en <1s. Hallazgo: race condition.
- 4. /tdd asume a partir de aquí — red: escribe una prueba que active dos workers simultáneos para el mismo payload. Falla (duplica).
- 5. /tdd — green: implementa una idempotency key en la tabla de pagos. La prueba pasa.
- 6. /tdd — refactor + regression: limpia el código, mantiene la prueba como regresión permanente. El bug no vuelve.
🧭 Cuándo alternar
- • ¿Sabes qué quieres construir? → /tdd directamente
- • ¿Tienes un bug y sabes reproducirlo? → /diagnose hasta llegar a la causa raíz, luego /tdd para el fix
- • ¿Tienes un bug y NO sabes reproducirlo? → /diagnose empieza con instrumentación remota antes de cualquier fix
- • ¿Refactor con pruebas existentes? → queda en el green-refactor de /tdd
- • ¿Refactor SIN pruebas? → /tdd primero: cubre el comportamiento actual con pruebas; solo después, refactoriza
🏋️ Ejercicio práctico
Toma un bug abierto en tu proyecto, cualquiera, de preferencia uno que hayas estado evitando porque «es raro». Ejecuta /diagnose y anota cada etapa en un documento. Al final, tendrás el bug resuelto Y material de revisión para entender cómo los agentes manejan bugs.
📋 Guion del ejercicio
- 1. Elige el bug. Pega en el chat: descripción del problema, stack trace si lo tienes, contexto mínimo.
- 2. Ejecuta
/diagnosey sigue las 6 etapas SIN saltarte ninguna. - 3. En cada etapa, anota en un
diagnose-log.md: lo que hizo el agente, lo que descubrió, lo que todavía no estaba claro. - 4. Cuando llegues a la corrección, ANTES de aplicarla, escribe la prueba de regresión (paso 6 de /diagnose).
- 5. Ejecuta la prueba: tiene que fallar. Aplica la corrección. Ejecútala de nuevo: tiene que pasar.
- 6. Commit del fix + prueba juntos, con un mensaje que haga referencia a la causa raíz.
💡 Criterio de éxito
No es «el bug se resolvió». Es «sé demostrar que el bug se resolvió y tengo una prueba que avisará a gritos si vuelve». Si no puedes marcar estas dos casillas, repite el ejercicio.
📚 Resumen del Módulo
Siguiente módulo:
1.5 — Próximos pasos de la ruta de Fundamentos