tests-and-ai-coding-tools.html
< BACK Escritorio gastado con cuadernos, laptop abierta mostrando código, y una taza de té bajo la luz fresca de una ventana de Londres

Por qué volví a escribir pruebas (la IA me obligó)

A principios de 2022, tomé una decisión discreta: dejé de escribir pruebas unitarias para la mayoría de los proyectos de WordPress y Node que llegaban a Seahawk. Sin anunciarlo. Sin post en un blog. Simplemente... dejé. La justificación era sólida, pensé — estábamos entregando 15 a 20 sitios de clientes al mes, tenía tres desarrolladores más en rotación, y las pruebas que escribía se sentían como documentación que nadie leía. El QA manual atrapaba los errores reales. Las pruebas eran teatro.

Avance rápido a finales de 2023. GitHub Copilot había estado en mi editor durante aproximadamente ocho meses. También había empezado a usar Cursor en paralelo para cualquier cosa nueva. La velocidad era genuinamente notable. Pero algo comenzó a suceder. Errores aparecían en lugares que no había tocado. Lógica que se veía correcta estaba mal en casos extremos que nunca se me había ocurrido verificar. Y lo peor — la IA no tenía idea de que estaba mal. Escribió el código roto con la misma indentación segura de siempre.Cursor on the side for anything greenfield. The speed was genuinely remarkable. But something started happening. Bugs were appearing in places I hadn't touched. Logic that looked correct was wrong in edge cases I'd never thought to check. And the worst part — the AI had no idea it was wrong. It wrote the broken code with the same confident indentation it always does.

Fue entonces cuando retomé las pruebas.

---

El período en que dejé de probar (y por qué tenía sentido en ese momento)

¿Respuesta honesta? Para cierto tipo de proyecto, saltarse las pruebas fue la decisión correcta. Si estás construyendo un sitio de folleto de cinco páginas en WordPress, escribir pruebas PHPUnit para un plugin de formulario de contacto es teatro. Me mantengo en eso.was the right call. If you're building a five-page brochure site in WordPress, writing PHPUnit tests for a contact form plugin is theatre. I stand by that.

El pan de cada día de Seahawk durante mucho tiempo fue exactamente ese trabajo — alto volumen, complejidad relativamente baja, alcance bien definido. Un cliente te entrega un archivo de Figma, lo construyes, haces QA, lo lanzas. Los ciclos de retroalimentación eran cortos. Si algo se rompía, lo sabías en horas. Escribir pruebas para ese contexto es el equivalente del desarrollador a plastificar una nota adhesiva.

Pero generalicé esa lección demasiado agresivamente. Comencé a tratar todos los proyectos como sitios de folleto. Incluso los que tienen flujos de pago personalizados en WooCommerce. Incluso el panel de fintech que construimos a principios de 2023 para un cliente en Frankfurt — API REST completamente personalizada, autenticación JWT, tres niveles de permisos de usuario diferentes. Sin pruebas. Solo "QA manual cuidadoso". Eso fue arrogante, y nos pasó factura.all projects like brochure sites. Even the ones with custom WooCommerce checkout flows. Even the fintech dashboard we built in early 2023 for a client in Frankfurt — full custom REST API, JWT auth, three different user permission tiers. No tests. Just "careful manual QA." That was arrogant, and it bit us.

El proyecto de Frankfurt se lanzó con un bug de permisos que permitía a usuarios de nivel editor consultar datos de nivel administrador bajo una combinación específica de filtros. No lo detectamos hasta que su equipo interno ejecutó una revisión de seguridad seis semanas después del lanzamiento. Vergonzoso. Corregible. Pero el tipo de cosa que una prueba de integración básica habría señalado antes de que siquiera levantáramos un pull request.

---

Qué herramientas de codificación con IA realmente cambiaron

Aquí está lo que la mayoría de la gente se pierde cuando habla de Copilot o Cursor o sea cual sea el modelo que esté de moda este mes: el código se ve bien. Ese es el problema.looks right. That's the problem.

Cuando un desarrollador junior escribe código defectuoso, a menudo puedes ver la incertidumbre en él. Nombres de variables raros, un comentario que dice // no estoy seguro de esto, una función que claramente fue copiada y pegada dos veces. El código delata su propia fragilidad. El código de IA no. Es consistente en estilo, bien nombrado, y estructurado de una manera que se lee como intencional. La confianza es completamente cosmética.// not sure about this, a function that's clearly copy-pasted twice. The code telegraphs its own fragility. AI code doesn't. It's stylistically consistent, well-named, and structured in a way that reads as intentional. The confidence is entirely cosmetic.

Estudios del grupo de Interacción Humano-Computadora de Stanford han señalado que los desarrolladores que usan asistentes de IA tienden a sobre-confiar en el código generado en la primera lectura. Eso concuerda con mi propia experiencia. Echaría un vistazo a una función de 40 líneas que Copilot había escrito, pensaría "sí, es básicamente lo que yo habría escrito", y seguía adelante. A veces estaba bien. A veces había entendido silenciosamente mal lo que realmente necesitaba. have flagged that developers using AI assistants tend to over-trust generated code on first read. That tracks with my own experience. I would glance at a 40-line function Copilot had written, think "yeah, that's basically what I'd have written," and move on. Sometimes it was fine. Sometimes it had silently misunderstood what I actually needed.

El modo de fallo específico que seguía encontrando: lógica condicional alrededor de casos extremos que la IA no tenía razón para anticipar. Escribía una función que manejaba el camino feliz perfectamente y luego fallaba silenciosamente en entradas nulas, arrays vacíos, o formatos de fecha no estándar. Cosas que me habrían tomado treinta segundos pensar si hubiera escrito el código yo mismo, porque habría estado pensando mientras escribía.conditional logic around edge cases the AI had no reason to anticipate. It would write a function that handled the happy path perfectly and then quietly fail on null inputs, empty arrays, or non-standard date formats. Things that would have taken me thirty seconds to think about if I'd written the code myself, because I'd have been thinking as I typed.

La Trampa de la Velocidad

Hay una trampa de productividad real aquí. La IA te hace rápido. La rapidez se siente como calidad. Empiezas a enviar más rápido y empiezas a revisar menos cuidadosamente porque la velocidad se siente como evidencia de calidad. No lo es. La velocidad y la corrección no están correlacionadas cuando estás usando un modelo de lenguaje.

Puse aproximadamente 40% más características en un proyecto de cliente el septiembre pasado de lo que hubiera logrado sin asistencia de IA. El proyecto también tuvo más bugs post-lanzamiento que cualquier cosa que hubiera enviado en dos años. No fueron bugs catastróficos. Pero sí molestos. El tipo que erosiona la confianza del cliente.

---

Por Qué Las Pruebas Funcionan Diferente Ahora (Con IA en el Ciclo)

Cuando volví a las pruebas, no volví al flujo de trabajo antiguo. Escribir pruebas primero, luego la implementación, luego revisión de código asistida por IA — ese es el ciclo en el que me he establecido ahora.

Lo interesante es que la IA es realmente excelente escribiendo pruebas, de una manera que no siempre lo es escribiendo lógica de aplicación. Dale a Copilot una firma de función bien definida y pídele que genere una suite de pruebas y producirá cobertura de casos límite que me hubiera tomado veinte minutos escribir manualmente. Imagina bien los caminos infelices cuando la tarea es específicamente "encontrar formas en que esto puede romperse".AI is actually excellent at writing tests, in a way it isn't always excellent at writing application logic. Give Copilot a well-defined function signature and ask it to generate a test suite and it'll produce edge-case coverage I'd have taken twenty minutes to write manually. It imagines unhappy paths well when the task is specifically "find ways this can break."

Así que he invertido la cosa de cierta forma. Escribo la especificación de prueba. La IA completa los casos de prueba. Luego la IA escribe la implementación. Luego leo la implementación a través del lente de esas pruebas, en lugar de solo leer el código a frio.through the lens of those tests, rather than just reading the code cold.

Es más lento que la programación pura por intuición. Pero es más rápido que el flujo de trabajo antiguo de escribirlo todo manualmente incluyendo pruebas. Y no ha enviado cero bugs de permisos desde Frankfurt.

Las Herramientas Que Realmente Estoy Usando

  • [Vitest](https://vitest.dev) para cualquier cosa en JavaScript o TypeScript. Reemplazó Jest completamente para mí el año pasado — la configuración es más sensata y el modo watch es rápido. for anything JavaScript or TypeScript. Replaced Jest for me entirely last year — the config is saner and the watch mode is quick.
  • PHPUnit aún, para WordPress y trabajo PHP personalizado. Nada lo ha reemplazado. still, for WordPress and custom PHP work. Nothing has replaced it.
  • El atajo "test this function" de Cursor — genuinamente una de las características individuales más útiles en cualquier editor que haya usado. — genuinely one of the most useful single features in any editor I've used.
  • GitHub Actions para CI. Las pruebas se ejecutan en cada push a main. Toma alrededor de 90 segundos en la mayoría de proyectos. for CI. Tests run on every push to main. Takes about 90 seconds on most projects.

---

El Argumento Contra las Pruebas (Presentado de Forma Justa)

Quiero darle a esta posición una consideración justa porque la sostuve durante casi dos años.

El argumento real no es "las pruebas son inútiles." Es "las pruebas tienen un costo y muchos proyectos no justifican ese costo." Escribir y mantener un conjunto de pruebas toma tiempo. En un proyecto con una vida útil corta — un micrositio de campaña, una página de destino de marketing, un prototipo de hackathon — esa inversión de tiempo no tiene retorno. El proyecto estará muerto antes de que las pruebas te salven de algo.

Y hay un punto más sutil: las malas pruebas son peor que ninguna prueba. Un conjunto de pruebas que pasa porque las pruebas son tautológicas (esencialmente estás verificando que tu función devuelva lo que le dijiste que devuelva) te da una confianza falsa. He visto esto en agencias. Desarrolladores escribiendo pruebas que siempre pasan porque nadie cuestionó lo que realmente estaban verificando.bad tests are worse than no tests. A test suite that passes because the tests are tautological (you're essentially testing that your function returns what you told it to return) gives you false confidence. I've seen this at agencies. Developers writing tests that always pass because nobody challenged what they were actually verifying.

Martin Fowler ha escrito bien sobre esto — los porcentajes de cobertura no son una medida de la calidad de las pruebas. Un número de cobertura del 90% puede enmascarar un conjunto completamente vacío. — coverage percentages are not a measure of test quality. A 90% coverage number can mask a completely hollow suite.

Entonces: no pruebes todo. No pruebes porque se sienta profesional. Prueba porque identificaste lógica crítica que sería costoso romper.

---

Qué Pruebo Ahora (Y Qué No)

Esta es la decisión real a la que llegué después de los últimos ocho o nueve meses:

Pruebo:

  1. Cualquier función que maneje dinero, permisos o transformación de datos
  2. Cualquier endpoint de API que no sea un simple pass-through CRUD
  3. Lógica comercial personalizada donde el cliente especificó el comportamiento exacto por escrito
  4. Cualquier cosa que escribió una IA que no leí completamente línea por línea

No pruebo:

  • Renderizado de UI (Las pruebas de snapshot nunca me han salvado ni una sola vez en nueve años. Ni una sola vez.)
  • Envoltorios de API de terceros donde el comportamiento externo está fuera de mi control
  • Scripts únicos que se ejecutan una vez y se eliminan
  • Hooks estándar de WordPress a menos que hagan algo inusual

Eso es todo. Sin filosofía grandilocuente. Solo una lista basada en dónde me he quemado.

---

El Flujo de Trabajo Que Realmente Funciona Para Mí

Ya que algunas personas han preguntado en las comunidades de Slack en las que estoy, aquí está la secuencia real:

  1. Escribo un comentario de especificación breve en la parte superior del archivo — qué hace este módulo, qué no hace, casos edge que ya conozco.
  2. Le pido a Cursor que genere casos de prueba a partir de ese comentario antes de escribir ninguna implementación.
  3. Revisa esos casos de prueba. Elimina los tontos. Agrega cualquiera que la IA haya pasado por alto.
  4. Deja que Copilot o Cursor escriba la implementación.
  5. Ejecuta las pruebas. Fallarán. Arregla la implementación (no las pruebas).
  6. Lee el diff antes de hacer push — el código asistido por IA todavía necesita revisión humana.

El paso 6 es innegociable. He atrapado tres bugs genuinamente graves en los últimos cuatro meses solo leyendo el diff lentamente antes de hacer push. Nada sofisticado. Solo leer.

El planteamiento original de TDD de Kent Beck nunca fue sobre cobertura del 100% o metodología perfecta. Se trataba de construir un ciclo de retroalimentación lo suficientemente rápido para atrapar errores antes de que se compungan. Esa idea — ciclos de retroalimentación rápidos — es más relevante ahora que lo que era en 2003. Porque la IA comete errores más rápido que cualquier desarrollador que haya contratado. was never about 100% coverage or perfect methodology. It was about building a feedback loop fast enough to catch mistakes before they compound. That idea — fast feedback loops — is more relevant now than it was in 2003. Because the AI makes mistakes faster than any developer I've ever hired.

---

FAQ

¿Esto ralentiza tu velocidad de entrega?

Aproximadamente 10 a 15% en proyectos complejos. En los simples, quizá nada — la IA genera las pruebas tan rápido que el overhead es mínimo. Para proyectos donde un bug costaría dinero real para arreglar después del lanzamiento (y la mayoría de proyectos de dinero real califican), ese 15% vale cien veces más.

¿Y qué hay con TypeScript? ¿No es que el tipado fuerte reemplaza muchas pruebas?

Parcialmente. TypeScript detecta toda una clase de errores en tiempo de compilación que antes necesitabas pruebas para encontrar. Pero los tipos no prueban la lógica de negocio. No verifican que tu función de cálculo de descuentos aplique las reglas correctas para clientes mayoristas. Eso sigue siendo tu responsabilidad.

¿Deberían los desarrolladores junior usar herramientas de codificación con IA si no están escribiendo pruebas?

No. Opinión muy firme. Un desarrollador junior usando Copilot sin pruebas es esencialmente pilotar un avión en piloto automático sin entender cómo funciona el piloto automático ni cómo aterrizar manualmente. La IA producirá código que se ve de nivel senior, el junior no sabrá en qué partes desconfiar, y eventualmente tendrás un incidente en producción. Las pruebas al menos les dan un mecanismo para verificar el resultado que están aceptando.

¿Por qué dejaste de hacer pruebas en primer lugar, honestamente?

Burnout, parcialmente. Y un período donde cada proyecto era genuinamente simple y las pruebas genuinamente no estaban dando su valor. El error fue no notar cuándo cambió la complejidad del proyecto y ajustarse en consecuencia. Esa es la lección real — no "siempre prueba" o "nunca pruebes" sino saber en qué categoría cae un proyecto dado.

---

Escribir pruebas no solía sentirse como protección. Se sentía como papeleo. La IA cambió eso. No porque la IA sea mala — me ha hecho significativamente más rápido — sino porque introdujo una nueva clase de errores confiados, bien formateados, y plausibles que no puedo detectar leyendo código de la manera en que solía hacerlo. Las pruebas no son para la IA. Son para mí. Una función obligatoria para pensar en qué realmente necesito que haga el código antes de aceptar lo que el modelo me entrega.

Desearía haberlo encuadrado de esa manera hace dos años.

< BACK