PTENES
MÓDULO 1.4

✅ Código que funciona: TDD + Diagnose

Red, green, refactor: con el agente a tu lado. Cómo usar TDD y el loop /diagnose para garantizar que el código realmente funcione, en vez de solo "parecer correcto" para el agente.

9
Temas
45
Minutos
Intermedio
Nivel
Práctica
Tipo
1

🤔 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.

Evidencia
prueba en ejecución > "parece correcto"
Criterio
verde = listo
Antibucle
la prueba evita el ciclo infinito
Contrato
la prueba es la especificación
2

🔄 /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.
Disciplina
una etapa a la vez
Ver fallar
la prueba tiene que fallar antes
Mínimo
no optimices en verde
Red
refactor protegido por pruebas
3

🍰 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.

Demostrable
cada slice que se pueda mostrar
End-to-end
prueba la integración ahora
Comentarios
descubre pronto qué falla
Contexto
el agente se enfoca en una feature
4

🔬 /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.

1

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.

2

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.

3

Hypothesise

Enumera las hipótesis posibles. No vayas directamente a la primera que parece buena: escribe 3-5 y ordénalas por probabilidad.

4

Instrument

Agrega logs, prints, breakpoints que demuestren o refuten cada hipótesis. No lo corrijas todavía: solo mídelo.

5

Corrección

Ahora sí, con la causa confirmada, escribe la solución. Mínima, enfocada, sin aprovechar para «también arreglar esto de aquí».

6

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.

Repro
verlo suceder = comienzo
Minimiza
elimina todo lo que no importa
Mide
probar antes de corregir
Bloqueo
la prueba evita regresiones
5

🎯 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.

Prueba
la repro muestra que existe
Medida
la misma prueba mide el fix
Antiadivinanzas
sin repro = sin certeza
Accionable
la repro se convierte en prueba después
6

🛡️ 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
Bloqueo
el bug no vuelve sin una alerta
Memoria
documenta la causa raíz
Barato
la repro ya existe, se convierte en prueba
Acumulativo
la suite se convierte en un blindaje
7

🔀 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. 1. /diagnose — reproduce: no puede. Retrocede un paso: instrumenta producción (logs de transaction id, timestamp, request id).
  2. 2. /diagnose — plantea una hipótesis: ¿retry del cliente? ¿race condition en el worker? ¿webhook duplicado del gateway?
  3. 3. /diagnose — instrument + minimise: los registros muestran que dos workers toman el mismo job cuando el cliente reintenta en <1s. Hallazgo: race condition.
  4. 4. /tdd asume a partir de aquí — red: escribe una prueba que active dos workers simultáneos para el mismo payload. Falla (duplica).
  5. 5. /tdd — green: implementa una idempotency key en la tabla de pagos. La prueba pasa.
  6. 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
Complementarias
no elige uno, usa los dos
Handoff
diagnose se lo entrega a tdd
Repro→Test
el puente entre ambos
Definitivo
fix + test = blindaje
8

🏋️ 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. 1. Elige el bug. Pega en el chat: descripción del problema, stack trace si lo tienes, contexto mínimo.
  2. 2. Ejecuta /diagnose y sigue las 6 etapas SIN saltarte ninguna.
  3. 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. 4. Cuando llegues a la corrección, ANTES de aplicarla, escribe la prueba de regresión (paso 6 de /diagnose).
  5. 5. Ejecuta la prueba: tiene que fallar. Aplica la corrección. Ejecútala de nuevo: tiene que pasar.
  6. 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.

Concreto
bug real, no un ejemplo
Documentado
registro de cada etapa
Verificable
la prueba demuestra la corrección
Reutilizable
el registro se convierte en una referencia futura

📚 Resumen del Módulo

✓
El agente necesita evidencia objetiva — "parece correcto" no cuenta; una prueba en ejecución sí.
✓
/tdd: red → green → refactor — un paso a la vez, ver que la prueba falle antes de implementar.
✓
Vertical slices > horizontales — feature completa de principio a fin, que se pueda mostrar al final de cada slice.
✓
/diagnose: 6 pasos para errores difíciles — reproduce, minimiza, plantea hipótesis, instrumenta, corrige, prueba la regresión.
✓
Reproducir antes de corregir — sin repro, el "fix" es una conjetura; con repro, la prueba mide la corrección.
✓
Bug corregido = prueba de regresión — si no hay una prueba, el bug vuelve. Estadística, no pesimismo.
✓
/diagnose y /tdd se complementan — diagnose encuentra la causa; tdd implementa el fix definitivo.

Siguiente módulo:

1.5 — Próximos pasos de la ruta de Fundamentos