Monday, June 22, 2026

Por qué la arquitectura importa con IA

La inteligencia artificial está cambiando la forma en que construimos software. Hoy es posible generar controladores, servicios, repositorios, pruebas unitarias e incluso propuestas completas de arquitectura en cuestión de segundos. Sin embargo, esta velocidad también introduce un nuevo riesgo: producir grandes cantidades de código incorrecto a una velocidad sin precedentes.

Muchos desarrolladores están descubriendo que la calidad del software generado por IA depende menos del modelo utilizado y más de la arquitectura sobre la cual se construye el sistema. Cuando la arquitectura es clara, las herramientas de IA tienden a producir resultados consistentes. Cuando la arquitectura es difusa o inexistente, la IA suele amplificar los problemas existentes.

Por esta razón, antes de hablar de prompts, agentes o generación de código, es necesario hablar de arquitectura. En un entorno donde una parte importante del código puede ser producida automáticamente, la arquitectura deja de ser un detalle técnico y se convierte en el principal mecanismo de control.

El problema de dejar que un agente programe sin reglas claras

Imaginemos que le pedimos a una IA que implemente una nueva funcionalidad en una aplicación ASP.NET Core:


Implementa el caso de uso para registrar un cliente.

La solicitud parece razonable, pero deja demasiadas decisiones abiertas. El agente deberá decidir dónde colocar la lógica, cómo validar los datos, cómo acceder a la base de datos y cómo manejar errores. En ausencia de restricciones explícitas, tomará decisiones basadas en los patrones más frecuentes encontrados durante su entrenamiento.

El resultado suele funcionar desde una perspectiva técnica. El código compila, las pruebas básicas pasan y la funcionalidad parece correcta. Sin embargo, es común encontrar lógica de negocio dentro de controladores, dependencias directas hacia Entity Framework, validaciones dispersas y un fuerte acoplamiento entre capas.

El problema no es que la IA haya cometido un error. El problema es que nunca recibió reglas claras sobre cómo debía construir la solución.

Cuando un desarrollador senior incorpora a un nuevo miembro al equipo, normalmente le explica la arquitectura, los patrones utilizados y las convenciones del proyecto. Con los agentes ocurre exactamente lo mismo. La diferencia es que la IA ejecutará las instrucciones con una velocidad mucho mayor, tanto para bien como para mal.

La IA genera código acoplado por defecto

Existe una característica importante que muchos equipos descubren después de utilizar IA durante varios meses: los modelos tienden a generar soluciones fuertemente acopladas cuando no reciben restricciones arquitectónicas.

Esto ocurre porque las implementaciones más comunes encontradas en internet suelen priorizar la rapidez sobre la separación de responsabilidades. Como consecuencia, es frecuente obtener ejemplos donde la lógica de negocio depende directamente de frameworks, bibliotecas externas o detalles de infraestructura.

Por ejemplo, una implementación típica podría verse así:


public async Task<IActionResult> Create(CreateOrderRequest request)
{
    var order = new Order();

    if(request.Items.Count == 0)
        return BadRequest();

    _dbContext.Orders.Add(order);

    await _dbContext.SaveChangesAsync();

    return Ok();
}

A primera vista no parece haber ningún problema. Sin embargo, la lógica de negocio, las validaciones, el acceso a datos y la capa web están mezclados dentro de la misma operación.

Cuando este patrón se replica cientos o miles de veces dentro de una aplicación, aparecen dificultades para realizar pruebas, evolucionar el dominio o reemplazar tecnologías específicas.

La IA no está intentando sabotear la arquitectura. Simplemente está optimizando para producir una solución funcional utilizando los patrones más frecuentes disponibles.

El verdadero riesgo no es el código incorrecto

Cuando se habla de IA en desarrollo de software, muchas conversaciones se enfocan en errores sintácticos o defectos funcionales. Sin embargo, esos problemas suelen ser relativamente fáciles de detectar mediante pruebas automatizadas y revisiones de código.

El riesgo más importante es otro: la erosión gradual de la arquitectura.

Cada vez que una nueva funcionalidad introduce dependencias innecesarias, mezcla responsabilidades o viola límites entre capas, la complejidad del sistema aumenta ligeramente. Individualmente, estos cambios parecen insignificantes. Acumulados durante meses o años, terminan convirtiéndose en una deuda técnica difícil de revertir.

La IA puede acelerar enormemente este proceso porque produce cambios con una velocidad mucho mayor que un desarrollador trabajando manualmente.

Por eso, el principal desafío no consiste en evitar que la IA genere código. Consiste en asegurarse de que el código generado respete las reglas arquitectónicas del sistema.

La arquitectura se convierte en un conjunto de guardarraíles

Una forma útil de entender la relación entre arquitectura e IA es pensar en la arquitectura como un sistema de guardarraíles.

Los guardarraíles no indican exactamente qué camino seguir. Su función es impedir que salgamos de la carretera.

Cuando una arquitectura está bien definida, limita las decisiones que pueden tomar tanto los desarrolladores como los agentes. Esto reduce la variabilidad de las implementaciones y aumenta la consistencia del sistema.

Por ejemplo, si la arquitectura establece que:

  • La lógica de negocio vive en el dominio.
  • El dominio no depende de frameworks.
  • La persistencia se accede mediante interfaces.
  • La infraestructura implementa contratos definidos por el dominio.

Entonces cualquier implementación generada por IA debe respetar esas reglas. El espacio de posibles soluciones se reduce y los resultados se vuelven mucho más predecibles.

Por qué la Arquitectura Hexagonal funciona especialmente bien con IA

Existen muchas arquitecturas válidas dentro del ecosistema .NET. Sin embargo, la Arquitectura Hexagonal presenta una característica especialmente interesante para los equipos que utilizan IA generativa: define fronteras extremadamente claras.

En una arquitectura hexagonal, el dominio ocupa el centro del sistema. Todo lo demás —bases de datos, APIs, colas de mensajes, frameworks y bibliotecas externas— se considera un detalle de infraestructura.

Esta separación proporciona instrucciones muy concretas para los agentes:

  • La lógica de negocio pertenece al dominio.
  • La infraestructura no define reglas de negocio.
  • Las dependencias apuntan hacia el interior.
  • Las integraciones externas se realizan mediante puertos y adaptadores.

Estas restricciones facilitan enormemente la generación de código consistente. En lugar de improvisar dónde colocar cada responsabilidad, el agente dispone de un conjunto claro de límites arquitectónicos.

Un ejemplo práctico en .NET

Supongamos que necesitamos agregar soporte para un nuevo proveedor de pagos en una aplicación de comercio electrónico.

En una arquitectura fuertemente acoplada, la lógica de integración suele terminar dispersa entre servicios, controladores y repositorios. Cada cambio implica modificar múltiples componentes.

En una arquitectura hexagonal, el dominio define un contrato:


public interface IPaymentGateway
{
    Task ProcessPaymentAsync(Payment payment);
}

La implementación concreta se ubica en infraestructura:


public class StripePaymentGateway : IPaymentGateway
{
    // Implementación específica
}

Cuando la IA recibe este contexto, entiende inmediatamente dónde debe colocar cada pieza. El dominio permanece estable y la infraestructura puede evolucionar sin afectar las reglas de negocio.

La arquitectura proporciona las restricciones necesarias para que la generación automática siga siendo sostenible a largo plazo.

La arquitectura es más importante que nunca

Durante años, muchos equipos consideraron la arquitectura como una preocupación secundaria frente a la implementación. En la era de la IA ocurre exactamente lo contrario.

La capacidad de producir código se está volviendo cada vez más barata. Lo que sigue siendo costoso es mantener sistemas comprensibles, evolutivos y alineados con las necesidades del negocio.

La arquitectura es el mecanismo que permite preservar esas propiedades cuando la velocidad de desarrollo aumenta drásticamente.

Cuanto más código pueda generar un agente, más importante será contar con límites claros que definan dónde debe vivir cada responsabilidad.

Conclusión

La inteligencia artificial no elimina la necesidad de una buena arquitectura. En realidad, la vuelve aún más importante.

Los agentes son extremadamente eficaces generando soluciones dentro de un marco de trabajo bien definido. Sin embargo, cuando las reglas son ambiguas o inexistentes, tienden a producir sistemas cada vez más acoplados y difíciles de mantener.

Por esta razón, la arquitectura debe considerarse el primer paso antes de generar una sola línea de código. Una buena arquitectura no limita el potencial de la IA; lo amplifica. Proporciona los guardarraíles necesarios para que la velocidad de ejecución no termine convirtiéndose en deuda técnica.

Y entre las distintas alternativas disponibles, la Arquitectura Hexagonal destaca porque ofrece precisamente lo que los agentes necesitan para trabajar de forma predecible: límites claros, responsabilidades bien definidas y una separación rigurosa entre el dominio y la infraestructura.

Friday, June 19, 2026

Stack y flujo paralelo: cómo trabajar en múltiples proyectos sin perder el control

Durante años, la mayoría de los desarrolladores hemos trabajado bajo una premisa simple: concentrarnos en una tarea a la vez. Terminamos una funcionalidad, luego pasamos a la siguiente. Terminamos un proyecto, luego iniciamos otro. Este enfoque ha funcionado razonablemente bien porque el desarrollo de software siempre estuvo limitado por la velocidad a la que una persona podía analizar problemas, escribir código y validar soluciones.

La aparición de herramientas basadas en inteligencia artificial está modificando esa realidad. Hoy es posible producir código, documentación, pruebas y propuestas de diseño a una velocidad muy superior a la que era común hace apenas unos años. Como consecuencia, muchos desarrolladores están descubriendo que pueden avanzar simultáneamente en varios frentes sin experimentar la misma carga cognitiva que antes.

Sin embargo, trabajar en paralelo no significa simplemente abrir más ventanas del IDE o participar en más proyectos al mismo tiempo. De hecho, hacerlo sin una estrategia clara suele generar el efecto contrario: más interrupciones, pérdida de contexto y una creciente sensación de desorden. La verdadera ventaja aparece cuando existe un sistema que permita mantener el control mientras se incrementa la capacidad de ejecución.

El problema del enfoque secuencial

En muchos equipos, el flujo de trabajo continúa siendo esencialmente lineal. Un desarrollador recibe una tarea, la analiza, la implementa, la prueba y finalmente pasa a la siguiente. Cuando aparece un bloqueo, gran parte del trabajo queda detenido hasta encontrar una solución.

Este modelo tiene sentido cuando la capacidad de producir código es el principal factor limitante. Sin embargo, cuando contamos con asistentes capaces de generar implementaciones, pruebas unitarias, consultas SQL o documentación en segundos, el cuello de botella se desplaza hacia otras actividades como la toma de decisiones, la validación y la gestión del contexto.

En otras palabras, la velocidad para escribir código deja de ser el principal desafío. El nuevo desafío consiste en coordinar múltiples flujos de trabajo sin perder visibilidad sobre lo que está ocurriendo en cada uno de ellos.

Qué significa realmente trabajar en paralelo

Trabajar en paralelo no implica realizar varias actividades al mismo tiempo de forma caótica. Significa mantener múltiples iniciativas avanzando de manera controlada, aprovechando los tiempos muertos y reduciendo los períodos de espera.

Por ejemplo, un desarrollador senior podría estar trabajando simultáneamente en:

  • Una nueva funcionalidad para una API ASP.NET Core.
  • La modernización de un sistema legado.
  • La documentación técnica de un servicio interno.
  • La investigación de una nueva arquitectura.

Tradicionalmente, gestionar estas actividades requería cambios constantes de contexto que resultaban costosos. Con IA, muchas tareas pueden delegarse temporalmente mientras el desarrollador continúa avanzando en otros frentes.

El objetivo no es aumentar la cantidad de trabajo, sino reducir el tiempo improductivo entre una actividad y otra.

El nuevo cuello de botella: el contexto

Cuando un desarrollador trabaja en varios proyectos, el problema más frecuente no es la implementación. El problema es recordar dónde quedó cada iniciativa, cuáles fueron las decisiones tomadas y qué restricciones existen en cada sistema.

Muchos equipos intentan resolver esto confiando en la memoria individual. Sin embargo, este enfoque se vuelve insostenible cuando el número de proyectos aumenta.

La IA amplifica este problema porque acelera la ejecución. Es posible avanzar más rápido, pero también es posible perder contexto más rápidamente si no existe una estructura adecuada.

Por esta razón, los desarrolladores más efectivos están comenzando a tratar el contexto como un activo del proyecto y no como un conocimiento almacenado únicamente en la mente de las personas.

Construyendo un stack de trabajo sostenible

Cuando hablamos de stack en este contexto, no nos referimos únicamente a tecnologías como .NET, SQL Server o Azure. Nos referimos al conjunto de herramientas, documentos y procesos que permiten gestionar el conocimiento necesario para desarrollar software de manera consistente.

Un stack de trabajo sostenible suele incluir varios elementos complementarios.

Documentación de arquitectura

Cada proyecto debería contar con una descripción clara de su arquitectura. No es necesario producir documentación extensa ni diagramas complejos. Lo importante es registrar las decisiones fundamentales para que cualquier persona —o cualquier agente de IA— pueda comprender rápidamente cómo está construido el sistema.

Por ejemplo:

  • Patrón arquitectónico utilizado.
  • Dependencias principales.
  • Reglas de comunicación entre capas.
  • Convenciones de diseño relevantes.

Registro de decisiones técnicas

Muchas veces el problema no es entender cómo funciona una solución, sino comprender por qué fue construida de determinada manera.

Mantener un historial simple de decisiones técnicas evita que las mismas discusiones se repitan constantemente y proporciona contexto valioso cuando se retoma un proyecto después de varias semanas.

Por ejemplo:

  • Por qué se eligió MediatR.
  • Por qué se descartó Event Sourcing.
  • Por qué se utiliza una determinada estrategia de caché.

Prompts reutilizables

A medida que los equipos incorporan IA en su flujo de trabajo, comienzan a aparecer patrones de interacción repetitivos. En lugar de escribir instrucciones desde cero cada vez, resulta útil construir una biblioteca de prompts reutilizables.

Por ejemplo:

  • Generación de pruebas unitarias.
  • Revisión de código.
  • Análisis de arquitectura.
  • Creación de documentación técnica.

Esto reduce la variabilidad de los resultados y mejora la consistencia entre proyectos.

Cómo mantener el control mientras aumenta la velocidad

Uno de los mayores riesgos al trabajar con IA es avanzar demasiado rápido sin verificar adecuadamente lo que se está produciendo. La velocidad puede generar una falsa sensación de progreso.

Por esta razón, resulta importante separar claramente las actividades de generación y validación.

La IA puede generar propuestas, implementaciones o pruebas. Sin embargo, la validación debe seguir siendo una responsabilidad explícita del desarrollador.

Un flujo saludable podría ser:

  1. Definir el problema.
  2. Solicitar alternativas de solución.
  3. Generar una implementación inicial.
  4. Revisar la propuesta críticamente.
  5. Ejecutar pruebas automatizadas.
  6. Validar el comportamiento esperado.

Este enfoque permite mantener la calidad sin sacrificar velocidad.

Un ejemplo práctico en un equipo .NET

Imaginemos un equipo que mantiene varios productos basados en ASP.NET Core. Mientras una nueva funcionalidad está siendo implementada, también es necesario corregir defectos en producción, mejorar la observabilidad del sistema y evaluar una futura migración tecnológica.

Con un enfoque tradicional, cada iniciativa compite por la atención de los desarrolladores. Con un flujo paralelo bien estructurado, las tareas pueden avanzar simultáneamente mediante ciclos cortos de trabajo apoyados por IA.

Mientras se revisa una propuesta de arquitectura para una migración, la IA puede ayudar a generar pruebas para una funcionalidad en desarrollo. Mientras esas pruebas se ejecutan, puede colaborar en la documentación de un servicio existente. El desarrollador mantiene el control de las decisiones importantes mientras delega actividades de menor valor estratégico.

El resultado no es simplemente más velocidad. El resultado es una mejor utilización del tiempo disponible.

La ventaja competitiva no es programar más rápido

Muchas conversaciones sobre IA se centran exclusivamente en la generación de código. Sin embargo, la verdadera transformación está ocurriendo en la forma en que organizamos el trabajo.

Los desarrolladores que obtendrán mejores resultados no serán necesariamente quienes generen más líneas de código por hora. Serán aquellos que construyan sistemas de trabajo capaces de coordinar múltiples iniciativas, preservar contexto y mantener estándares de calidad incluso cuando la velocidad de ejecución aumente.

La capacidad de gestionar conocimiento se está convirtiendo en una habilidad tan importante como la capacidad de programar.

Conclusión

La inteligencia artificial está reduciendo significativamente el costo de producir software. Como consecuencia, muchas de las limitaciones tradicionales del desarrollo están desapareciendo. Sin embargo, nuevos desafíos están tomando su lugar.

El contexto, la coordinación y la toma de decisiones se están convirtiendo en los factores que determinan la productividad real de un equipo. Trabajar en múltiples proyectos ya no requiere necesariamente más esfuerzo, pero sí requiere mejores sistemas.

Un stack de trabajo bien diseñado, combinado con un flujo paralelo controlado, permite aprovechar las capacidades de la IA sin caer en el desorden. La meta no es hacer más cosas al mismo tiempo. La meta es construir un entorno donde varias iniciativas puedan avanzar simultáneamente sin perder claridad, calidad ni control.

TDD adaptado para IA

Durante más de dos décadas, Test-Driven Development (TDD) ha sido una de las prácticas más influyentes dentro del desarrollo de software profesional. Su propuesta es simple: escribir una prueba que falle, implementar el código mínimo para hacerla pasar y luego refactorizar la solución manteniendo el comportamiento esperado.

En el ecosistema .NET, TDD ha ayudado a construir sistemas más mantenibles, mejorar el diseño del código y reducir la cantidad de defectos que llegan a producción. Sin embargo, la aparición de asistentes de inteligencia artificial está cambiando algunas de las condiciones bajo las cuales nació esta práctica.

Hoy podemos pedirle a una IA que genere una prueba unitaria, implemente una funcionalidad completa y proponga una refactorización en cuestión de segundos. Como consecuencia, el tiempo dedicado a escribir código se ha reducido drásticamente. El nuevo desafío ya no es producir código más rápido, sino asegurarnos de que dicho código realmente resuelve el problema correcto.

Por esta razón, muchos equipos están descubriendo que el ciclo tradicional de TDD sigue siendo valioso, pero necesita ciertos ajustes para funcionar eficazmente en un entorno donde los agentes de IA participan activamente en el desarrollo.

Por qué el TDD clásico no encaja completamente con la IA

El ciclo tradicional de TDD fue diseñado para desarrolladores que escriben código manualmente. La secuencia es conocida:


Red
Green
Refactor

Primero se escribe una prueba que falla. Luego se implementa el código mínimo necesario para hacerla pasar. Finalmente se mejora el diseño sin alterar el comportamiento observable.

Cuando trabajamos con IA, esta dinámica cambia. Una sola solicitud puede producir simultáneamente pruebas, implementación y refactorización. En muchos casos, la IA es capaz de completar en segundos un trabajo que anteriormente requería varios ciclos de desarrollo.

El problema es que la velocidad puede ocultar errores. Una implementación puede compilar correctamente, pasar algunas pruebas básicas y aun así incumplir reglas de negocio importantes. Cuanto más rápido genera código la IA, más importante se vuelve contar con mecanismos sólidos de validación.

Por eso, el foco deja de estar en la escritura del código y pasa a estar en la definición precisa del comportamiento esperado.

De Test-Driven Development a Specification-Driven Development

Cuando incorporamos IA al proceso, resulta útil pensar en una evolución natural de TDD. En lugar de comenzar directamente por la implementación, debemos dedicar más esfuerzo a construir especificaciones claras que sirvan como contrato entre el desarrollador y el agente.

La secuencia práctica suele parecerse más a lo siguiente:


Especificación
↓
Pruebas
↓
Implementación
↓
Validación

La especificación se convierte en el artefacto principal. Cuanto más precisa sea, mejores serán las pruebas generadas y más confiable será la implementación producida por la IA.

En otras palabras, el desarrollador deja de invertir la mayor parte de su tiempo escribiendo código y comienza a invertir más tiempo definiendo comportamientos, restricciones y criterios de aceptación.

Un ejemplo práctico en .NET

Imaginemos que estamos desarrollando un sistema de pedidos utilizando ASP.NET Core y Entity Framework Core.

Un enfoque tradicional podría consistir en pedir a la IA:


Implementa un endpoint para crear pedidos.

Aunque la solicitud parece razonable, deja demasiadas decisiones abiertas. ¿Qué validaciones deben aplicarse? ¿Cómo se manejan los errores? ¿Qué ocurre si el cliente no existe?

Un enfoque basado en especificaciones sería mucho más explícito:


Crear un pedido debe cumplir las siguientes reglas:

- El cliente debe existir.
- El pedido debe contener al menos un producto.
- No se permiten cantidades menores o iguales a cero.
- El total debe calcularse automáticamente.
- Si alguna validación falla, debe devolverse un error de dominio.

A partir de esta descripción, la IA puede generar pruebas unitarias mucho más útiles y producir una implementación alineada con las necesidades reales del negocio.

Las pruebas se convierten en contratos

Uno de los mayores riesgos al trabajar con IA es asumir que una solución es correcta porque compila o porque parece razonable durante una revisión rápida. La experiencia demuestra que muchas implementaciones aparentemente válidas contienen errores sutiles.

Por esta razón, las pruebas deben evolucionar de simples verificaciones técnicas a contratos explícitos de comportamiento.

Por ejemplo, una prueba como esta aporta poco valor:


result.Should().NotBeNull();

En cambio, una prueba que valida una regla de negocio específica resulta mucho más útil:


[Fact]
public async Task Should_Reject_Order_When_No_Items_Exist()
{
    // Arrange

    // Act

    // Assert
}

Las pruebas más valiosas son aquellas que describen claramente las reglas que el sistema debe respetar. Esto permite detectar rápidamente cuando una implementación generada por IA se desvía del comportamiento esperado.

Cómo evitar pruebas generadas de poco valor

Otro problema frecuente es pedir a la IA que genere pruebas sin proporcionar suficiente contexto. El resultado suele ser una colección de pruebas superficiales que verifican propiedades triviales y ofrecen poca protección frente a errores reales.

Por ejemplo, muchas pruebas generadas automáticamente terminan validando únicamente que un método devuelve algún valor o que una instancia no es nula.

Un desarrollador senior debe orientar a la IA hacia escenarios que realmente representen riesgos para el negocio:

  • Casos límite.
  • Errores de validación.
  • Reglas complejas de dominio.
  • Condiciones de concurrencia.
  • Manejo de excepciones.
  • Escenarios de integración.

Las pruebas deben proteger comportamientos importantes, no simplemente aumentar el porcentaje de cobertura.

Usando la IA para encontrar escenarios olvidados

Una de las aplicaciones más interesantes consiste en utilizar la IA para descubrir casos de prueba que el equipo no había considerado inicialmente.

Por ejemplo, después de definir una funcionalidad, podemos solicitar:


Analiza este caso de uso y genera escenarios
que podrían provocar errores funcionales,
problemas de concurrencia o inconsistencias de datos.

Con frecuencia aparecen situaciones que no fueron contempladas durante la fase de diseño. Esta capacidad convierte a la IA en un excelente complemento para actividades de revisión y análisis de riesgos.

La validación sigue siendo una responsabilidad humana

Uno de los errores más peligrosos consiste en asumir que una implementación es correcta porque fue generada junto con sus pruebas. Si la IA produce tanto el código como los tests, existe el riesgo de que ambos compartan los mismos supuestos incorrectos.

Por esta razón, la validación debe mantenerse como una actividad consciente del desarrollador. Las pruebas ayudan a verificar comportamientos, pero la responsabilidad de determinar si dichos comportamientos representan correctamente las necesidades del negocio sigue recayendo en el equipo.

La IA puede acelerar enormemente el proceso de construcción, pero no reemplaza el conocimiento del dominio ni el criterio técnico necesario para tomar decisiones arquitectónicas.

Un ciclo de trabajo práctico para equipos .NET

Una adaptación efectiva de TDD para equipos que utilizan IA podría seguir estos pasos:

  1. Definir claramente la especificación funcional.
  2. Identificar reglas de negocio y restricciones.
  3. Generar pruebas basadas en dichas reglas.
  4. Solicitar la implementación utilizando las pruebas como contrato.
  5. Revisar manualmente la solución.
  6. Ejecutar pruebas automatizadas.
  7. Refactorizar manteniendo el comportamiento esperado.

Este enfoque conserva los principios fundamentales de TDD mientras aprovecha las capacidades de generación y análisis que ofrecen las herramientas de IA modernas.

Conclusión

TDD sigue siendo una práctica extremadamente relevante en la era de la inteligencia artificial. Sin embargo, el contexto ha cambiado. La generación de código ya no representa el principal costo dentro del proceso de desarrollo.

Hoy el verdadero desafío consiste en definir correctamente el comportamiento esperado y validar que las implementaciones respeten las reglas del negocio. En este escenario, las especificaciones y las pruebas adquieren todavía más importancia.

Los equipos que logren adaptar TDD a un flujo de trabajo asistido por IA podrán aprovechar la velocidad de estas herramientas sin sacrificar calidad, mantenibilidad ni control sobre sus sistemas. El objetivo ya no es escribir más código. El objetivo es construir software correcto con mayor confianza y en menos tiempo.

Pair Programming con IA

Durante años, el pair programming ha sido una de las prácticas más efectivas para mejorar la calidad del software y compartir conocimiento dentro de los equipos. Tradicionalmente, dos desarrolladores trabajan juntos frente al mismo problema: uno asume el rol de Driver, escribiendo el código, mientras que el otro actúa como Navigator, revisando decisiones, identificando riesgos y manteniendo una visión global de la solución.

La llegada de los asistentes de inteligencia artificial está cambiando este modelo. Hoy es posible generar código, pruebas unitarias, documentación e incluso propuestas de arquitectura en cuestión de segundos. Sin embargo, muchos desarrolladores siguen utilizando la IA como si fuera simplemente un autocompletado avanzado. El resultado suele ser inconsistente: algunas veces la IA produce excelentes resultados y otras veces genera código incorrecto o difícil de mantener.

Para aprovechar realmente estas herramientas es necesario replantear la forma en que hacemos pair programming. La pregunta ya no es cómo escribir código más rápido, sino cómo colaborar eficazmente con un agente que puede asumir distintos roles dentro del proceso de desarrollo.

El problema de tratar a la IA como un desarrollador junior

Uno de los errores más frecuentes consiste en utilizar la IA como un desarrollador al que se le asignan tareas aisladas. Se le pide implementar una funcionalidad, generar una consulta SQL o crear una API sin proporcionar suficiente contexto sobre la arquitectura, las reglas de negocio o las convenciones del proyecto.

Cuando esto ocurre, la calidad de las respuestas depende demasiado del contexto disponible y de la precisión de las instrucciones. En algunos casos el resultado puede parecer correcto a simple vista, pero introducir inconsistencias que solo se detectan semanas después durante la integración o el mantenimiento.

La IA no posee conocimiento permanente sobre nuestro sistema. Su capacidad para ayudar depende directamente de la información que recibe. Por esa razón, el objetivo no debe ser delegar decisiones importantes, sino crear un flujo de trabajo que permita aprovechar sus fortalezas mientras mantenemos el control técnico.

Un nuevo modelo de colaboración

En lugar de pensar únicamente en Driver y Navigator, resulta más útil considerar que la IA puede desempeñar distintos roles según la actividad que estemos realizando. Cada rol tiene un propósito específico y puede ser invocado en diferentes momentos del desarrollo.

La IA como Arquitecto

Antes de escribir código, podemos utilizar la IA para explorar alternativas de diseño. En lugar de pedir una implementación inmediata, podemos solicitar un análisis de posibles enfoques, ventajas, desventajas y riesgos.

Por ejemplo, imaginemos una aplicación ASP.NET Core que debe procesar eventos provenientes de múltiples sistemas externos. Antes de implementar la solución, podríamos consultar:


Necesito procesar eventos provenientes de varios proveedores.
Analiza si conviene utilizar una arquitectura basada en colas,
event sourcing o procesamiento directo mediante APIs.
Considera mantenibilidad, escalabilidad y complejidad operativa.

La respuesta no debe tomarse como una decisión definitiva, pero sí como un punto de partida para evaluar alternativas más rápidamente.

La IA como Implementador

Este es probablemente el uso más popular. Una vez definida la estrategia, la IA puede generar código repetitivo o acelerar la implementación de componentes bien especificados.

Por ejemplo, después de diseñar un caso de uso, podemos solicitar la implementación de una clase siguiendo las convenciones del proyecto:


Implementa un comando CreateOrder utilizando MediatR,
Entity Framework Core y validaciones mediante FluentValidation.
La solución debe seguir Clean Architecture.

La diferencia clave es que la implementación ocurre después de haber definido claramente las reglas y restricciones. La IA recibe un marco de trabajo bien establecido, lo que reduce significativamente la probabilidad de obtener resultados inconsistentes.

La IA como Revisor

Uno de los usos más valiosos para desarrolladores senior consiste en utilizar la IA como un segundo par de ojos. En lugar de pedir que escriba código nuevo, podemos solicitar una revisión crítica de una implementación existente.

Por ejemplo:


Analiza este servicio de aplicación.
Identifica posibles problemas de concurrencia,
violaciones de SOLID, deuda técnica y riesgos de rendimiento.

La IA puede detectar oportunidades de mejora que pasen desapercibidas durante una revisión rápida. No reemplaza el criterio del desarrollador, pero amplía la capacidad de análisis.

La IA como Tester

Otro rol especialmente útil consiste en pedir a la IA que intente romper nuestra solución. En lugar de enfocarse en los escenarios esperados, puede ayudarnos a identificar casos límite, validaciones faltantes o situaciones excepcionales.

Por ejemplo:


Analiza este endpoint y genera escenarios que podrían
provocar errores funcionales, problemas de seguridad
o comportamientos inesperados.

Este enfoque suele revelar escenarios que no fueron considerados durante el diseño inicial.

Cómo evitar depender del estado de ánimo del modelo

Muchos desarrolladores experimentan resultados inconsistentes al trabajar con IA. Una misma solicitud puede producir respuestas excelentes un día y mediocres al siguiente. En realidad, el problema rara vez está relacionado con el modelo; normalmente está relacionado con la falta de un proceso definido.

Cuando cada interacción comienza desde cero, la calidad depende demasiado de la conversación actual. Por el contrario, cuando existe una estructura clara, los resultados tienden a ser mucho más predecibles.

En proyectos .NET resulta especialmente útil mantener artefactos que proporcionen contexto estable:

  • Documentación de arquitectura.
  • Convenciones de codificación.
  • Patrones utilizados por el equipo.
  • Ejemplos de código de referencia.
  • Reglas de negocio relevantes.

Cuanto más consistente sea el contexto entregado a la IA, más consistentes serán las respuestas obtenidas.

La responsabilidad sigue siendo del desarrollador

Una idea equivocada que aparece con frecuencia es pensar que la IA reducirá la necesidad de experiencia técnica. En la práctica está ocurriendo exactamente lo contrario. Los desarrolladores con mayor conocimiento de arquitectura, diseño y calidad de software son quienes más valor están obteniendo de estas herramientas.

La IA puede acelerar la generación de soluciones, pero sigue siendo necesario evaluar decisiones técnicas, validar supuestos y comprender las consecuencias de cada cambio. Un código generado automáticamente no deja de ser código que deberá mantenerse durante años.

Por esa razón, el objetivo no debe ser delegar el trabajo intelectual, sino aumentar nuestra capacidad para analizar problemas, explorar alternativas e implementar soluciones con mayor velocidad.

Conclusión

El pair programming con IA no consiste en reemplazar al desarrollador ni en automatizar completamente la programación. Consiste en incorporar un colaborador capaz de asumir distintos roles según la necesidad del momento.

Cuando la IA actúa como arquitecto, implementador, revisor o tester, se convierte en una herramienta mucho más poderosa que un simple generador de código. La clave está en diseñar un flujo de trabajo donde cada interacción tenga un propósito claro y donde el desarrollador mantenga siempre el control de las decisiones técnicas.

Los equipos que aprendan a trabajar de esta manera no solo escribirán software más rápido. También podrán tomar mejores decisiones, detectar problemas antes y mantener sistemas complejos con mayor eficiencia.

Sunday, June 14, 2026

Cómo evitar la sobreingeniería cuando trabajas con agentes

Uno de los problemas más frecuentes al trabajar con agentes no es que produzcan soluciones incorrectas.

Muchas veces ocurre exactamente lo contrario.

La solución funciona.

Pero es innecesariamente compleja.

Lo que podría resolverse con una función termina convertido en un framework. Lo que requería una clase termina distribuido en múltiples capas. Lo que necesitaba una consulta SQL termina envuelto en patrones, abstracciones y componentes adicionales.

Si has trabajado algunos días con agentes de desarrollo, probablemente ya has visto este comportamiento.

El modelo no siempre busca la solución más simple.

Con frecuencia busca la solución más completa.

Y ambas cosas no son equivalentes.

Por qué los agentes tienden a sobreingenierizar

Los modelos fueron entrenados con enormes cantidades de código, documentación técnica, artículos de arquitectura, patrones de diseño y proyectos open source.

Como resultado, suelen conocer muchas formas sofisticadas de resolver un problema.

Cuando el requerimiento es ambiguo, el agente tiende a asumir escenarios futuros que nadie pidió.

Empieza a prepararse para:

  • Escalabilidad futura.
  • Posibles extensiones.
  • Nuevos tipos de usuarios.
  • Casos hipotéticos.
  • Requerimientos que todavía no existen.

En otras palabras, comienza a resolver problemas imaginarios.

El resultado suele ser más código, más complejidad y más mantenimiento.

La regla más importante: resolver el problema actual

Existe una técnica extremadamente efectiva para reducir la sobreingeniería.

Consiste en obligar al agente a trabajar únicamente sobre el problema presente.

Por ejemplo:

Resuelve únicamente el requerimiento descrito. No agregues extensiones, puntos de configuración, abstracciones o funcionalidades para posibles necesidades futuras.

Esta instrucción parece simple.

Pero cambia radicalmente el comportamiento del modelo.

La mayoría de los excesos aparecen cuando el agente intenta anticipar el futuro.

Eliminar esa necesidad reduce gran parte de la complejidad innecesaria.

Haz que justifique cada capa adicional

Una práctica útil consiste en pedir al agente que justifique cualquier elemento que incremente la complejidad de la solución.

Por ejemplo:

Toda nueva abstracción debe tener una justificación clara. Si no existe un beneficio inmediato y demostrable, utiliza la alternativa más simple.

Esto obliga al modelo a pensar antes de agregar:

  • Interfaces.
  • Patrones de diseño.
  • Capas de servicios.
  • Configuraciones adicionales.
  • Mecanismos de extensibilidad.

Muchas veces descubrirá que no son necesarios.

Pide siempre la solución más simple primero

Los agentes suelen responder con la primera solución que consideran técnicamente sólida.

Pero no necesariamente es la más sencilla.

Por eso resulta útil agregar una restricción explícita:

Antes de implementar, identifica la solución más simple que cumpla completamente el requerimiento.

Esta pequeña instrucción suele generar resultados sorprendentemente mejores.

Obliga al modelo a evaluar simplicidad como un criterio de calidad.

No únicamente corrección técnica.

La complejidad tiene un costo

Cuando un desarrollador escribe código adicional, normalmente piensa en el beneficio que aporta.

Pero muchas veces olvida el costo que introduce.

Lo mismo ocurre con los agentes.

Cada nueva capa implica:

  • Más código que mantener.
  • Más puntos de fallo.
  • Más pruebas.
  • Más tiempo de comprensión.
  • Más dificultad para nuevos miembros del equipo.

La pregunta correcta no es:

¿Puede hacerse?

Sino:

¿Realmente necesitamos hacerlo?

Desconfía de las arquitecturas para un futuro hipotético

Una de las señales más claras de sobreingeniería aparece cuando el agente comienza a prepararse para escenarios que todavía no existen.

Por ejemplo:

  • Diseñar para múltiples proveedores cuando solo existe uno.
  • Crear sistemas de plugins cuando no hay extensiones planificadas.
  • Implementar arquitecturas distribuidas para una aplicación pequeña.
  • Agregar decenas de configuraciones que nunca serán modificadas.

Este comportamiento suele parecer profesional.

Pero muchas veces genera más problemas de los que resuelve.

La mejor arquitectura para hoy suele ser la que resuelve el problema de hoy.

Utiliza el principio YAGNI

Existe un principio muy conocido dentro del desarrollo de software:

YAGNI (You Aren't Gonna Need It).

Su idea es simple.

No implementes algo hasta que realmente sea necesario.

Este principio funciona extraordinariamente bien con agentes.

Puedes convertirlo en una regla explícita:

Aplica YAGNI en todas las decisiones de diseño. No implementes funcionalidades, extensiones o puntos de configuración que no sean requeridos actualmente.

Muchos equipos descubren que esta única instrucción reduce significativamente la complejidad generada por la IA.

La simplicidad también es una característica de calidad

Existe una creencia equivocada en ingeniería de software.

Algunas personas asumen que una solución compleja demuestra mayor capacidad técnica.

La experiencia suele mostrar lo contrario.

Las mejores soluciones suelen ser aquellas que:

  • Son fáciles de entender.
  • Son fáciles de probar.
  • Son fáciles de modificar.
  • Resuelven exactamente el problema requerido.

La simplicidad no es falta de ingeniería.

Es una forma de ingeniería.

Conclusión

Los agentes tienen una enorme capacidad para construir soluciones sofisticadas.

Pero la sofisticación no siempre aporta valor.

Cuando trabajes con agentes, recuerda que el objetivo no es producir la solución más impresionante.

El objetivo es producir la solución correcta con la menor complejidad posible.

Definir límites claros, aplicar YAGNI, exigir justificaciones para cada capa adicional y priorizar la simplicidad permite obtener sistemas más mantenibles y más fáciles de evolucionar.

Porque en ingeniería de software, la mejor solución rara vez es la más compleja.

Generalmente es la más simple que resuelve el problema.

Límites y estructura: cómo trabajar con agentes sin perder el control

En el artículo anterior vimos 8 técnicas de prompting que ayudan a obtener mejores resultados de un modelo. Sin embargo, cuando comenzamos a utilizar agentes para tareas más complejas, aparece un problema diferente.

Ya no basta con escribir buenos prompts.

Ahora necesitamos diseñar un entorno donde los agentes trabajen con reglas claras, responsabilidades definidas y límites explícitos.

La diferencia es similar a la que existe entre un desarrollador escribiendo una función y un equipo completo trabajando sobre un producto.

Mientras más autonomía tenga el sistema, más importante se vuelve la estructura.

El problema de la libertad excesiva

Cuando empezamos a trabajar con agentes solemos asumir que más libertad significa mejores resultados.

La realidad suele ser la contraria.

Un agente con demasiada libertad puede:

  • Modificar cosas que no debía tocar.
  • Tomar decisiones fuera de su responsabilidad.
  • Cambiar arquitecturas completas para resolver problemas simples.
  • Generar soluciones inconsistentes entre sí.
  • Desviarse del objetivo original.

En otras palabras, el agente no falla porque sea incapaz.

Falla porque no conoce los límites dentro de los cuales debe operar.

Lo mismo ocurriría con un desarrollador nuevo que recibe la instrucción:

Haz lo que creas conveniente.

Probablemente trabajará mucho, pero no necesariamente en la dirección correcta.

Los agentes funcionan mejor dentro de un sistema

Una idea común es pensar que el prompt es el centro del trabajo con IA.

En realidad, el prompt es solo una pieza.

Lo que produce resultados consistentes es el sistema completo:

  • Reglas.
  • Restricciones.
  • Roles.
  • Flujo de trabajo.
  • Criterios de aceptación.

Cuando observamos equipos de ingeniería maduros, vemos exactamente el mismo patrón.

Los desarrolladores no trabajan únicamente con instrucciones verbales.

Trabajan dentro de un conjunto de normas:

  • Convenciones de código.
  • Arquitectura definida.
  • Estándares de calidad.
  • Revisiones de código.
  • Pruebas automatizadas.

Los agentes también necesitan ese contexto.

Define responsabilidades explícitas

Uno de los errores más comunes consiste en asignar demasiadas responsabilidades a un único agente.

Por ejemplo:

Analiza el requerimiento, diseña la solución, implementa el código, escribe pruebas y revisa la arquitectura.

Puede funcionar.

Pero generalmente funciona mejor cuando cada responsabilidad está claramente separada.

Por ejemplo:

  • Agente Analista.
  • Agente Diseñador.
  • Agente Implementador.
  • Agente Revisor.

Cada uno recibe un objetivo específico.

Esto reduce ambigüedad y facilita detectar errores.

Cuando algo sale mal, resulta evidente dónde ocurrió el problema.

Las reglas deben ser concretas

Muchas personas escriben reglas demasiado generales.

Por ejemplo:

Escribe código de calidad.

¿Qué significa exactamente calidad?

La interpretación puede variar enormemente.

Una mejor regla sería:

  • No modificar archivos fuera del alcance de la tarea.
  • Mantener compatibilidad con las pruebas existentes.
  • No introducir nuevas dependencias sin justificación.
  • Seguir la arquitectura actual.
  • Generar pruebas automatizadas para cambios funcionales.

Mientras más observable sea una regla, más fácil será cumplirla.

Limita el espacio de decisión

No todas las decisiones deben quedar abiertas.

Supongamos que una organización utiliza:

  • ASP.NET Core
  • Entity Framework Core
  • SQL Server

No tiene sentido que el agente evalúe:

  • Node.js
  • Django
  • Spring Boot
  • PostgreSQL
  • MongoDB

Ese trabajo ya fue resuelto anteriormente por el equipo.

Una buena instrucción podría ser:

Las decisiones tecnológicas ya están tomadas. Trabaja únicamente dentro del stack existente.

Este tipo de restricciones reduce ruido y mejora la velocidad de ejecución.

La diferencia entre restricciones y microgestión

Algunas personas creen que limitar a un agente reduce su utilidad.

No necesariamente.

Existe una diferencia importante entre:

Restricciones

y

Microgestión.

Una restricción define el marco de trabajo.

Por ejemplo:

No modificar la arquitectura existente.

La microgestión intenta controlar cada detalle.

Por ejemplo:

Escribe exactamente 14 líneas en cada método y utiliza tres variables locales.

Las restricciones útiles reducen errores.

La microgestión reduce capacidad de adaptación.

Utiliza listas de verificación

Los agentes responden muy bien a checklists.

Antes de ejecutar una tarea, puedes pedirles validar condiciones específicas.

Por ejemplo:

Antes de generar código:

  • ¿Entendiste el requerimiento?
  • ¿Existe una solución más simple?
  • ¿La implementación respeta la arquitectura actual?
  • ¿Se requieren pruebas?
  • ¿Se está modificando algo fuera del alcance?

Este pequeño mecanismo suele prevenir muchos errores antes de que ocurran.

Piensa como un arquitecto de sistemas

Cuando trabajamos con agentes, dejamos de ser únicamente usuarios de IA.

Pasamos a diseñar sistemas donde la IA opera.

La pregunta deja de ser:

¿Cuál es el mejor prompt?

Y se transforma en:

¿Cuál es el mejor entorno para que este agente trabaje?

Ese cambio de mentalidad suele marcar una gran diferencia.

Los equipos que obtienen resultados consistentes no dependen de prompts mágicos.

Construyen estructuras claras donde los agentes pueden tomar decisiones sin salirse del camino.

Conclusión

La calidad de un agente no depende únicamente de su modelo.

También depende del sistema que construimos alrededor de él.

Definir límites claros, separar responsabilidades, establecer reglas concretas y reducir el espacio de decisión permite obtener resultados más predecibles y más útiles.

Los agentes no necesitan libertad absoluta.

Necesitan un marco de trabajo bien diseñado.

Y, al igual que ocurre en los equipos de ingeniería, las mejores soluciones suelen aparecer cuando existe suficiente autonomía para actuar, pero también suficiente estructura para mantener el rumbo.

Tuesday, June 9, 2026

8 técnicas de prompting que te ahorran bugs (y horas) cuando trabajas con agentes

En los artículos anteriores cerramos una idea: tu trabajo no es escribir más rápido, es decidir mejor. Aprendimos qué delegar y qué retener usando tres filtros —claridad, precisión y control— y repetimos el principio que sostiene toda la serie: conserva el pensamiento, delega la escritura.

Pero hay un problema práctico. Puedes tener clarísimo qué quieres delegar y aun así obtener basura, porque el prompt es el contrato entre tu pensamiento y el agente. Si el contrato es vago, el agente rellena los huecos con suposiciones, y esas suposiciones se convierten en bugs que tú vas a tener que cazar después.

Este artículo abre un nuevo bloque de la serie —técnicas, reglas y control— y empieza por lo más cercano al teclado: cómo pedir las cosas. No son trucos para "engañar" al modelo. Son hábitos para que el agente trabaje dentro de tus reglas y no contra ellas.

Por qué un buen prompt es prevención de bugs, no decoración

Un junior suele pensar en el prompt como una pregunta: "hazme un endpoint". Un desarrollador que trabaja en serio con agentes lo piensa como una especificación pequeña: contexto, restricciones y forma de salida.

La diferencia se nota en el coste. Un prompt vago genera código que compila pero que asume la versión equivocada del framework, mete una dependencia que no querías o rompe una convención de tu proyecto. Tú no lo ves al instante: lo descubres en code review, en un test que falla o —peor— en producción. Cada suposición no controlada del agente es deuda que pagas con tu tiempo.

Las ocho técnicas que siguen atacan justo eso: reducir el espacio de suposiciones para que el agente acierte a la primera.

1. Contexto antes que instrucción

El error más común del junior es ir directo a la orden: "créame un repositorio para usuarios". El agente no sabe en qué versión de .NET estás, qué ORM usas ni cómo organizas las capas, así que adivina —y suele adivinar lo más popular en su entrenamiento, no lo que tú tienes.

Pon el contexto primero, en una o dos líneas:

Stack: .NET 10, ASP.NET Core, EF Core 9, arquitectura por capas
(Domain / Application / Infrastructure / Web).
Tarea: crea un repositorio para la entidad Experto.

Qué bug evitas: código para una versión que no usas, APIs obsoletas (o aún no disponibles) y patrones que no encajan con tu arquitectura. El contexto recorta de golpe el 80% de las suposiciones malas.

2. Define qué significa "terminado"

"Hazme la búsqueda de expertos" no tiene un final claro. ¿Incluye paginación? ¿Filtra por categoría? ¿Devuelve un DTO o la entidad? El agente decidirá por ti, y rara vez como tú querías.

Dale criterios de aceptación, igual que harías en una historia de usuario:

Terminado cuando:
- Busca expertos por nombre o categoría (coincidencia parcial).
- Devuelve una lista de ExpertoDto, no la entidad de dominio.
- Soporta paginación (page, pageSize), con valores por defecto 1 y 20.
- Si no hay resultados, devuelve lista vacía, nunca null.

Qué bug evitas: el clásico NullReferenceException por devolver null en vez de lista vacía, y rondas enteras de "no, en realidad necesitaba que también…". Definir el final por adelantado es la forma más barata de no iterar cinco veces.

3. Da un ejemplo de tu propio código (few-shot)

Los modelos imitan muy bien. Si le pegas un ejemplo de cómo ya escribes tú, el resultado se parecerá a tu código y no al de un tutorial random de internet.

Antes de pedir un repositorio nuevo, pega uno que ya tengas:

Sigue exactamente este estilo (nombres, async, manejo de IQueryable):

public class CategoriaRepository : ICategoriaRepository
{
    private readonly AppDbContext _db;
    public CategoriaRepository(AppDbContext db) => _db = db;

    public async Task<Categoria?> GetByIdAsync(int id) =>
        await _db.Categorias.FindAsync(id);
}

Ahora hazme el ExpertoRepository con los mismos criterios.

Qué bug evitas: inconsistencias de estilo que ensucian el code review, mezcla de patrones (un método síncrono aquí, otro async allá) y convenciones de nombres que rompen con el resto del proyecto. Un ejemplo vale más que tres párrafos de instrucciones.

4. Restricciones explícitas: dile también qué NO hacer

Esta técnica es la que más sorprende a los juniors, porque parece contraintuitivo gastar tokens diciendo lo que no quieres. Pero los agentes tienden a "ayudar de más": meten una librería, refactorizan algo que no tocaba, cambian una firma pública.

Restricciones:
- No agregues paquetes NuGet nuevos.
- No uses AutoMapper; el mapeo a DTO va a mano.
- No cambies la firma de los métodos públicos existentes.
- No toques la configuración de DI.

Qué bug evitas: dependencias sorpresa que tú no decidiste, cambios fuera de alcance que rompen otras partes y refactors no pedidos que disparan tu diff de 10 a 200 líneas. Las restricciones son tu primer mecanismo de control —y, no por casualidad, el tema del próximo artículo.

5. Pensar antes de escribir: primero el plan, luego el código

Para lógica no trivial, pedir el código directamente es arriesgado: el agente se compromete con un diseño en la primera línea y luego "defiende" ese diseño aunque esté mal. Sepáralo en dos pasos.

Antes de escribir nada, explícame en 4-5 puntos cómo vas a
resolver la validación de la solicitud de búsqueda
(orden de las validaciones, qué devuelves ante cada error).
No escribas código todavía.

Lees el plan, lo corriges si hace falta, y recién entonces le dices "ok, impleméntalo". Aquí sigues siendo el navegante: el plan es donde está tu pensamiento, y revisarlo cuesta segundos frente a revisar 80 líneas de código equivocado.

Qué bug evitas: errores de diseño que se vuelven errores de implementación. Es mucho más barato corregir un plan en texto que un código ya escrito que parece correcto.

6. Descompón la tarea en pasos pequeños

"Hazme toda la funcionalidad de búsqueda de expertos, de la entidad al controlador" es pedir mucha superficie de una sola vez. Cuanto más grande el output, más difícil es revisarlo y más se esconden los bugs.

Ve capa por capa, en el mismo orden en que tú lo construirías:

  1. Entidad y configuración de EF Core.
  2. DTO de salida.
  3. Interfaz del repositorio.
  4. Implementación del repositorio.
  5. Servicio de aplicación.
  6. Controlador.
  7. Tests.

Revisas y das el visto bueno a cada paso antes de pasar al siguiente. Qué bug evitas: el efecto "muro de código" donde apruebas 300 líneas que no leíste de verdad. Pasos pequeños = revisión real = menos bugs colándose por cansancio.

7. Trata el test como especificación

Si ya trabajas con TDD o BDD, esta técnica encaja sola: pide el test primero (o junto con el código), porque el test es una forma ejecutable de decir qué significa "correcto".

Primero escribe los tests xUnit para BuscarExpertosAsync:
- Devuelve expertos que coinciden por nombre parcial.
- Devuelve lista vacía cuando no hay coincidencias.
- Respeta la paginación.
Luego implementa el método para que esos tests pasen.

Qué bug evitas: el código que "parece" funcionar pero no cubre los casos borde. Cuando el test define el contrato, el agente no puede irse por las ramas: tiene un objetivo verificable, no una interpretación libre. Además te quedas con la red de seguridad puesta para el refactor.

8. Feedback específico en la iteración

Cuando algo sale mal, el reflejo del junior es escribir "no funciona" o "sigue mal". Eso obliga al agente a adivinar otra vez, y suele cambiar cosas al azar hasta que algo encaja —o hasta que rompe más.

Dale el error exacto, igual que se lo darías a un compañero:

Lanza NullReferenceException en BuscarExpertosAsync, línea 24,
cuando _db.Expertos no tiene registros. El Where devuelve un
IQueryable vacío, pero luego haces .First() en vez de
.FirstOrDefault(). Corrige solo eso.

Qué bug evitas: las "correcciones" que generan tres bugs nuevos. Un error concreto produce un arreglo concreto; un "no anda" produce una ruleta. Y fíjate en el detalle final —"corrige solo eso"— que es otra vez una restricción para que no aproveche para refactorizar de más.

El hilo que conecta las ocho

Si las relees, todas hacen lo mismo: reducen lo que el agente tiene que suponer. Contexto, criterios de "terminado", ejemplos, restricciones, plan previo, pasos pequeños, tests y feedback exacto son ocho maneras de cerrar huecos antes de que se conviertan en bugs.

Y todas son coherentes con el principio de la serie. No estás escribiendo menos para pensar menos; estás escribiendo prompts precisos porque ahí es donde vive tu pensamiento. El agente teclea; tú decides qué teclear.

Estas técnicas funcionan a nivel de cada interacción. Pero cuando trabajas con agentes de forma seria —varios pasos, varias sesiones, un proyecto real como una API en ASP.NET Core— necesitas algo más que buenos prompts sueltos: necesitas límites y estructura que el agente respete siempre, sin que tengas que repetirlos cada vez. De eso trata el próximo artículo.