Programación Asistida por IA
Técnicas prácticas para entregar software real con agentes de programación por IA.
Los ADR son combustible para agentes de IA
Los Architecture Decision Records le dan a los asistentes de código con IA lo único que tu código no puede: por qué elegiste lo que elegiste. Formato, flujo de trabajo y beneficio.
Tip #002El detector de alucinaciones de la IA en 5 segundos
El código generado por IA puede llamar a APIs que nunca existieron. Un verificador de tipos en modo de solo comprobación lo detecta en segundos — tsc --noEmit para TypeScript, pyright para Python.
Tip #003Ejecuta dos agentes de IA a la vez con git worktrees
Dos agentes de IA en una misma carpeta se sobrescriben los archivos. Los git worktrees le dan a cada agente su propio directorio y su propia rama sobre el mismo repositorio — aquí está la configuración.
Tip #101Revisa primero los tests y divide los diffs grandes
Lee los tests antes que la implementación para entender la intención primero — y mantén los diffs cerca de 100 líneas, dividiendo lo que se acerque a 1,000 por apilado, grupo de archivos o corte vertical.
Tip #102¿Refactor grande? Escribe un codemod, no ediciones a mano
Cuando un refactor toca cientos de líneas, deja de editar a mano: escribe un codemod, un script sed o una transformación de AST para que una sola regla se aplique en todas partes y el diff siga siendo revisable.
Tip #103Ese código feo sostiene la estructura
La cerca de Chesterton para refactorizar con IA: ejecuta git blame y responde 3 preguntas antes de dejar que un agente borre código que no entiende.
Tip #104Los retornos tempranos matan el anidamiento; las constantes con nombre matan los números mágicos
Dos refactorizaciones de treinta segundos que hacen legible el código generado por IA: convierte los ifs anidados en cláusulas de guarda y extrae los números sin explicación a constantes con nombre.
Tip #105Nunca conviertas una variable de entorno a bool con un cast
bool(\“false\”) es True — un cast directo invierte tu kill switch en silencio. Parsea los booleanos con un vocabulario compartido de true/false para env vars y config.
Tip #106Un valor de configuración malformado debe registrarse y usar el valor por defecto
Pasa cada ajuste por un único lector tipado que registra el valor inválido y degrada a un valor por defecto documentado — para que un typo sea una línea de log, no una caída.
Tip #107Result y Wait son deadlocks en espera
Por qué bloquear con .Result o .Wait produce deadlocks en apps web C# bajo carga — y cómo ir async hasta el fondo, con cancellation tokens donde importan.
Tip #109Tu sitio grita hecho por IA: tres señales que puedes buscar con grep
El look de 'AI slop' se detecta con grep: gradientes morados, todo centrado y un único radio de 16px+ — tres comandos lo encuentran, tres arreglos lo rompen.
Tip #110outline: none sin un estilo de foco de reemplazo
Una línea de CSS deja fuera a los usuarios de teclado — cómo :focus-visible les devuelve el anillo de foco sin ensuciar la UI del mouse.
Tip #111El estado del servidor va en una capa de caché, no en el estado global
Los datos que pertenecen al backend no van en tu store global: ponlos en una capa de caché de datos y obtén caching, deduplicación e invalidación gratis.
Tip #112Las UIs generadas por IA se saltan los estados vacíos — pide cada estado en el prompt
La IA construye el camino feliz de tu UI; tus usuarios viven en el camino triste. Checklist para pedir estados de carga, vacío, error y desborde — siempre.
Tip #113Nunca lances una goroutine sin dueño
Las goroutines no se recolectan como basura: cada `go` en código Go generado por IA necesita una ruta de cancelación, un dueño del canal y un WaitGroup, o se fuga hasta tumbar el servicio.
Tip #115Usa ?? para valores por defecto, no ||
Por qué `config.port || 3000` descarta en silencio 0, cadena vacía y false — y cómo el operador nullish coalescing (`??`) solo aplica el fallback con null/undefined.
Tip #116forEach no espera nada
Por qué un callback async dentro de forEach se adelanta en silencio — y cómo elegir deliberadamente entre for...of + await y Promise.all.
Tip #117Rechaza el doble bang en Kotlin generado
Por qué cada !! en Kotlin generado por IA es un crash diferido, y cómo los nullables explícitos y los resultados sealed hacen imposible omitir el caso vacío.
Tip #118Prohíbe GlobalScope.launch en los code reviews
Las corrutinas sin scope filtran trabajo y se tragan la cancelación — convierte GlobalScope.launch en un rechazo automático de review y ata cada corrutina a un dueño.
Tip #121Ese inocente chequeo de `if exists` es una condición de carrera
Por qué el patrón chequear-y-actuar sobre el filesystem es una carrera TOCTOU, y cómo un solo parámetro — exist_ok=True — elimina el bug y una línea de código a la vez.
Tip #122sum() transmite en flujo perezoso, sum([]) construye primero
Quita los corchetes dentro de sum() y un pico de memoria de un millón de elementos se convierte en un flujo perezoso — mismo resultado, un carácter menos.
Tip #123¿Dos ifs en una comprensión? Dale un nombre de función
Cuando a una list comprehension le crece una segunda condición, expándela en una función generadora con nombre: legible, testeable y un patrón que tu agente de código va a copiar.
Tip #124Los threads no arreglan tu Python limitado por CPU
Threads o asyncio para trabajo limitado por I/O, ProcessPoolExecutor para trabajo limitado por CPU: la decisión de una línea que los asistentes de IA fallan, y cómo saber qué límite estás enfrentando.
Tip #125String += en un loop es O(n²), usa join
Por qué += sobre strings dentro de un loop es cuadrático, y las dos soluciones lineales: ''.join() sobre una lista de partes, o io.StringIO para construcción incremental.
Tip #126Encadena siempre las excepciones con 'from e'
Relanzar una excepción envuelta sin 'from e' entierra el traceback original — dos palabras mantienen la traza apuntando a la causa real.
Tip #127Protocol en vez de herencia: costuras de prueba estructurales en Python
Usa typing.Protocol para reemplazar dependencias reales por fakes de prueba sin acoplamiento a clases base — tipado estructural que el checker sigue verificando.
Tip #130Rechaza los callbacks de Rails generados por IA que hacen trabajo lento
Los asistentes de IA adoran los after_save que llaman APIs — una regla de revisión mantiene los saves rápidos, los tests sin red y el trabajo lento reintentándose en jobs.
Tip #131Impón la unicidad en la base de datos, no solo en el modelo
validates_uniqueness_of de Rails es propenso a condiciones de carrera bajo carga concurrente: úsalo para errores amigables, pero agrega un índice único en la base de datos para tener la garantía.
Tip #132Todo bloque unsafe necesita un comentario de seguridad
Un bloque unsafe suspende las verificaciones del compilador — escribe un comentario // SAFETY que declare el invariante que sostiene, y deja que revisores y agentes de IA verifiquen en vez de adivinar.
Tip #133Nunca Construyas Comandos de Shell Como Cadenas
Una sola variable sin comillas puede borrar el directorio equivocado. Usa arreglos de Bash para las listas de argumentos, pon comillas en cada expansión y arranca los scripts con set -euo pipefail.
Tip #134Cambia la paginación por offset por la paginación por keyset
En una tabla grande y cambiante, LIMIT/OFFSET salta y repite filas en silencio — la paginación por keyset avanza usando la clave de la última fila y se mantiene consistente aunque la tabla mute debajo de ti.
Tip #135Audita el Swift generado por IA en busca de Task.detached
¿Ves Task.detached en Swift generado por IA? Detente y revísalo — una tarea detached descarta silenciosamente la cancelación, la prioridad y el aislamiento de actor, y casi siempre es la opción equivocada.
Tip #136Ponle fecha de vencimiento a cada supresión: ts-expect-error en vez de ts-ignore
@ts-ignore calla para siempre; @ts-expect-error da error apenas se corrige el bug de fondo, así las supresiones de tipos no se pudren en tu código.
Tip #55Comenta el porqué, nunca el qué
Borra los comentarios que repiten tu código y conserva el único tipo que nunca se pudre: los comentarios de porqué, que registran la intención para humanos y asistentes de IA.
Tip #56Nunca dejes código comentado
Borra el código muerto en vez de comentarlo: git recuerda cada línea, y tu asistente de IA razona mejor sobre archivos sin cementerios.
Tip #57Mantén un archivo de reglas para tu agente
Documenta tus convenciones una sola vez en un archivo de reglas — CLAUDE.md, AGENTS.md, .cursor/rules — y cada sesión de IA las sigue sin que tengas que reescribir nada.
Tip #58Documenta las trampas inline, donde muerden
El bug que te costó un día merece un comentario justo donde la próxima persona va a caer — no en una wiki que nadie abre.
Tip #59La documentación ahora es contexto ejecutable
Nadie leía tu documentación — hasta que tu agente de código empezó a leer cada palabra. Por qué los registros de decisiones son el contexto de mayor impacto que puedes escribir, y cómo mantener uno en 10 minutos.
Tip #60La sección de alternativas del ADR es oro
Registra las opciones que rechazaste — pros, contras y una línea sobre por qué perdió cada una — para que tu agente de código con IA deje de proponer ideas que ya descartaste.
Tip #61Incorpora la IA a un código nuevo como a un humano
Dale a tu agente de código el mismo onboarding que a un nuevo integrante — un orden fijo de reconocimiento en cinco pasos más un request rastreado — para que su código respete tus convenciones.
Tip #62Rastrea una petición real de punta a punta
La forma más rápida de entender cualquier codebase: sigue una petición real desde el punto de entrada hasta la base de datos y de vuelta — y luego entrégale ese mapa a tu agente de IA.
Tip #63Registra el dialecto local para que el código de IA se integre
Una checklist de 3 puntos — nombres, patrones, reglas de la casa — para que el código generado por IA coincida con tu codebase y no con un default genérico.
Tip #64La regla SMIG: Situación, Mecanismo, Implicación, Gotcha
Una regla de cuatro partes para docs de onboarding y code tours: omite lo que el código ya muestra, invierte tus palabras en implicaciones y gotchas.
Tip #65No vuelques el árbol de archivos y lo llames onboarding
Un listado de carpetas le dice a tu agente de código qué existe, no sobre qué actuar — reemplaza el volcado crudo con cinco movimientos narrados que trazan el camino real por el sistema.
Tip #66Archivos .tour: recorridos de código por persona
Convierte el onboarding en una ruta guiada: los archivos .tour anclan recorridos paso a paso a archivos y líneas reales, un tour por persona, reproducidos dentro del editor.
Tip #67Verifica cada afirmación contra el código
Los README se desactualizan; el código no. Cómo hacer onboarding a cualquier repo trazando una request real de punta a punta y verificando cada afirmación contra el código fuente.
Tip #68Ejecuta tu suite de pruebas nativas bajo sanitizers
Por qué ASan, UBSan y TSan atrapan los bugs de C/C++ que un reporte verde de cobertura de líneas deja pasar — y cómo integrarlos a las pruebas que ya tienes.
Tip #69Atrapa cada crash antes de arreglarlo
Escribe el test de regresión que falla antes del arreglo, córrelo bajo un sanitizer, y el bug no puede volver en silencio.
Tip #70No confíes en los tests con base de datos en memoria
Los fakes de base de datos en memoria se saltan la traducción a SQL: la consulta que pasa en tests puede romperse en producción. Cuándo y cómo probar contra el motor real.
Tip #71Verifica los cuatro estados de tu UI
La mayoría de los tests de widgets solo verifican el render feliz: aquí te mostramos cómo probar loading, vacío, error y éxito, con los patrones de mocking que hacen alcanzable cada estado.
Tip #72Nunca uses waitForTimeout — espera la red real
Las esperas fijas hacen que los tests E2E sean lentos Y frágiles a la vez — reemplaza cada una por una espera sobre la condición real: una respuesta de red o una aserción con reintentos automáticos.
Tip #73Haz que un test inestable falle diez veces a propósito
Reproduce un test inestable en tu máquina con --repeat-each=10 (y sus equivalentes en pytest y Go) en vez de quemar una semana de reintentos en CI.
Tip #74Inyecta relojes, semillas aleatorias y directorios temporales
Los tests inestables casi siempre leen una entrada oculta: el reloj real, un RNG compartido o un directorio temporal común. Inyecta los tres y las mismas entradas darán el mismo resultado en cada ejecución.
Tip #75Pruebas por capas, no un arranque completo
Por qué @SpringBootTest debería ser la excepción y no el default — usa slices como @WebMvcTest y @DataJpaTest para feedback más rápido, y enséñale el mismo hábito a tu asistente de IA.
Tip #76Mezcla el orden de tu suite de tests para exponer tests dependientes del orden
Ejecuta tu suite en orden aleatorio para detectar bugs de estado compartido en tu máquina — con los flags exactos de shuffle y seed para Vitest, Jest, pytest, Go y RSpec.
Tip #77Un test que pasa contra código vacío no prueba nada
Si tu suite sigue en verde después de borrar el código de producción, no verifica nada. La auditoría de cinco minutos, los placeholders sospechosos y las herramientas que automatizan el chequeo.
Tip #78Nunca lances corrutinas reales en los tests
Por qué los dispatchers reales hacen que los tests asíncronos fallen de forma intermitente en CI, y cómo los TestDispatchers inyectados, el tiempo virtual y los tests del ciclo de vida completo de Flow los vuelven deterministas.
Tip #79Un Fixture, Tres Bases de Datos
Parametriza un solo fixture de pytest con SQLite, PostgreSQL y MySQL y cada test que lo usa corre una vez por backend — 3x de cobertura con un decorador.
Tip #80Etiqueta los tests lentos y recupera tu ciclo de trabajo
Usa markers de pytest para separar los tests de integración lentos de los tests unitarios rápidos: cada guardado corre en menos de un segundo y CI corre todo.
Tip #81Siempre haz mock con autospec=True
Un Mock plano acepta métodos que no existen — parchea con autospec=True para que tus tests fallen cuando el código generado por IA usa mal una API real.
Tip #82Mocks asíncronos: verifica el await, no la llamada
assert_called_once pasa aunque tu código haya olvidado el await — assert_awaited_once es la aserción que de verdad atrapa el bug.
Tip #83Nunca desactives los warnings en Pytest
Los deprecation warnings son señales tempranas de falla: por qué silenciar los warnings de pytest cambia una corrida ruidosa hoy por una caída en producción mañana, y cómo filtrar con precisión.
Tip #84pytest --lf: vuelve a ejecutar solo lo que falló
Reduce tu ciclo de depuración de minutos a segundos con pytest --lf, -x, --ff y --sw — y por qué importa aún más cuando un agente de IA está depurando.
Tip #85100% de cobertura de tests es una señal de alerta, no una medalla
Por qué perseguir 100% de cobertura en toda la suite desperdicia esfuerzo — apunta a ~80% global y lleva auth, pagos y mutación de datos hacia 100% de cobertura de ramas.
Tip #86Prueba tus trabajos en segundo plano: idempotencia y reintentos
Las colas entregan al-menos-una-vez, así que todo trabajo terminará ejecutándose dos veces — aquí te mostramos cómo probar que una ejecución duplicada cobre una sola vez, envíe un solo correo y se recupere limpiamente tras un fallo.
Tip #87Los tests inestables se deben al tiempo, al IO o a la aleatoriedad
Un test que pasa a veces casi siempre toca tiempo real, archivos reales o aleatoriedad real — inyecta los tres y la inestabilidad desaparece.
Tip #88Haz tus docs ejecutables: doc tests
Compila los ejemplos de tu documentación como parte de la suite de tests — doc tests de Rust, doctest de Python y patrones de pytest que hacen que la documentación obsoleta rompa el CI.
Tip #89Pasa tus scripts de shell por ShellCheck primero
Analiza el bash generado por IA con ShellCheck antes de probarlo: la mayoría de los bugs de shell son problemas estáticos de comillas y portabilidad que un linter detecta en segundos.
Tip #90Prueba tus scripts de shell desde una ruta con espacios
Ejecuta tus scripts de shell desde un directorio temporal cuya ruta contiene un espacio: los bugs de comillas y de directorio actual fallan a lo grande, gratis, antes de fallar en producción.
Tip #91Prueba los invariantes de datos en la capa de la base de datos
La validación en la app no basta: prueba llaves únicas, llaves foráneas y check constraints en una base de datos real y desechable, igual a producción.
Tip #92Simula la red, la persistencia, los relojes — y el estado de permisos
Ya simulas la red y la base de datos en tus tests — inyecta también un fake para el estado de permisos, y la rama de denegado dejará de llegar rota a producción.
Tip #93Prueba el bug antes de arreglarlo
Haz que tu agente de código con IA escriba un test que falle y reproduzca el bug antes de tocar el arreglo: prueba de que existe, prueba de que se fue, y una guardia permanente.
Tip #94Que un subagente aparte escriba el test de reproducción
Separa el test de reproducción del fix: un subagente nuevo escribe el test que falla sin ver el arreglo, para que los puntos ciegos compartidos no aprueben un parche roto.
Tip #95El mock tautológico
Cuando un mock devuelve exactamente el valor que el test afirma, el test pasa sin ejecutar ni una línea de código de producción — cómo detectar mocks tautológicos y reemplazarlos con implementaciones reales o fakes en memoria.
Tip #96Mantén tus tests DAMP, no DRY
Por qué la duplicación está bien en los tests: DAMP (Descriptive And Meaningful Phrases) le gana a DRY cuando un test que falla tiene que explicarse solo.
Tip #97Haz que el agente descubra los comandos propios del proyecto
Evita que tu agente de IA adivine pytest o npm test: haz que lea package.json, pyproject.toml, el Makefile y el CI para encontrar los comandos canónicos del repo.
Tip #98Nunca confíes en tests escritos por IA para validar código escrito por IA
Cuando un mismo modelo escribe el código y sus tests, ambos pueden compartir la misma suposición errónea: una suite en verde prueba consistencia, no corrección. Así se rompe el ciclo.
Tip #99Un puntero crudo con ownership en C++ bloquea el code review
Por qué un puntero crudo con ownership debe reprobar el code review en C++, y la checklist RAII (unique_ptr, shared_ptr, span, regla de cero) para aplicarla al código generado por IA.
TipVerifica context.mounted después de cada await
Por qué las apps Flutter se caen después de llamadas asíncronas lentas — y la guarda de una línea con context.mounted que elimina el use-after-dispose.
TipUn modelo escribe, un modelo distinto revisa
Por qué el modelo que escribió tu código no puede revisarlo con confianza — y cómo un ciclo de dos modelos (escribir/revisar) con decisión humana final atrapa muchos más bugs.
TipJava Optional es solo un tipo de retorno
Optional fue diseñado para un solo trabajo: tipos de retorno de métodos. Por qué los campos, los parámetros y el .get() sin verificar pelean contra el diseño, y qué patrones usar en su lugar.