Configuración del entorno de trabajo para eliminar el código repetitivo obvio generado por agentes de IA
Cuando integras un agente de IA en un entorno de desarrollo real, sus limitaciones quedan al descubierto rápidamente. Al no conocer el contexto del dominio, tiende a abusar mecánicamente del patrón Factory o a generar infinitas funciones de utilidad totalmente inútiles. Un informe de análisis de 623 millones de líneas de commits publicado por GitClear en 2024 revela que, tras la adopción masiva de herramientas de IA, la duplicación de código aumentó un 81%, mientras que la proporción de refactorización para limpiar el código existente se desplomó del 25% a menos del 10%. Debido al sesgo de reciente en la ventana de contexto, el agente busca evitar riesgos huyendo continuamente hacia los patrones estándar más comunes de sus datos de entrenamiento. Es necesario mitigar el funcionamiento defectuoso del agente especificando las reglas de arquitectura y la lógica de sincronización compartidas por el equipo a través de archivos de contexto.
1. Bloquear la desviación de reglas en bases de código heredadas
Para controlar directrices que difieren en cada herramienta de desarrollo, primero debes colocar un AGENTS.md en la raíz del proyecto para que actúe como la única fuente de verdad. Herramientas como Cursor y Claude Code leen estas reglas. El archivo único .cursorrules fue descartado hace tiempo. En su lugar, divídelo en archivos MDC dentro del directorio .cursor/rules/, e importa @AGENTS.md dentro de CLAUDE.md para el entorno de Claude Code para evitar que las reglas se diluyan.
`yaml
description: Backend API and Domain Service architectural constraints
globs: apps/api//*.ts, services//*.ts
alwaysApply: false
Backend Architectural Constraints
Mandatory Patterns
- Use Canonical Response DTOs located in
apps/api/src/common/dto.
- All database mutations must go through the Unit of Work pattern defined in
services/shared/uow.
Explicit Negative Constraints
- NEVER create single-child base classes or single-implementation interfaces.
- NEVER write wrapper functions that merely pass arguments to lower-level services.
- NEVER implement strategy or factory patterns when standard conditional logic (if/switch) suffices.
- NEVER add defensive "just-in-case" try-catch blocks that return dummy fallback values without rethrowing.
`
Si la longitud total del contexto supera las 800 líneas, aproximadamente 2.000 tokens, el agente comienza a ignorar las restricciones escritas en la parte superior. En AGENTS.md solo se deben conservar las prohibiciones estrictas e irrenunciables, trasladando los registros detallados de decisiones arquitectónicas al directorio docs/context/.
Orden de aplicación de restricciones
- Detalle del trabajo: Seleccionar 1 módulo clave y definir los patrones prohibidos y las reglas de excepción.
- Método de ejecución:
- Crear
AGENTS.md en la raíz del proyecto y especificar las condiciones de prohibición para interfaces de implementación única y bloques try-catch defensivos.
- Configurar las rutas de archivos objetivo (
globs) y las restricciones de YAML Frontmatter en la ubicación .cursor/rules/backend-constraints.mdc.
- Añadir la sintaxis
@AGENTS.md a CLAUDE.md para alinear el entorno.
- Resultado esperado: Se reduce la generación de código repetitivo innecesario, disminuyendo el tiempo dedicado a la modificación de código en unas 4 horas a la semana.
2. Pipeline de validación de código generado por agentes
Detrás del código de apariencia plausible generado por el agente se esconden trampas. Según un estudio de Veracode de 2024, se encontraron vulnerabilidades de seguridad de nivel OWASP Top 10 en el 45% del código sugerido por IA. El acto de envolver superficialmente la lógica propensa a fallos en un try-catch y ocultar errores devolviendo un objeto vacío o null ocurre un 47% más a menudo que cuando lo escriben humanos. Omisiones de bloqueos optimistas en operaciones Read-Modify-Write o problemas de N+1 por llamadas continuas a la base de datos dentro de bucles también ocurren con frecuencia.
| Área de validación |
Ítem de validación detallado |
Patrón de riesgo y trampa del agente |
Criterio de bloqueo de merge |
| Vulnerabilidades de seguridad |
Uso de Parameterized Query, aislamiento de inquilinos (tenant), verificación de paquetes no autorizados |
Inyección SQL basada en concatenación de cadenas, llamadas a paquetes no verificados creados por alucinación |
Faltas en validación de entradas, bloqueo al añadir dependencias externas de origen no claro |
| Cuello de botella de rendimiento |
Carga perezosa (Lazy loading) de ORM, bucles anidados en Hot path, índices de BD |
Consultas de recorrido de entidades individuales dentro de bucles, filtrado de tablas completas en memoria |
Bloqueo si existen llamadas a BD o API externas en bucles, bloqueo si no se aplica paginación |
| Seguridad de tipos |
Validación de Strict Type, manejo de excepciones en Boundary, control de concurrencia |
Abuso de as any, ocultación de errores con bloques Catch vacíos |
Bloqueo si existe any o casteos indiscriminados con as, bloqueo de bloques Catch sin registro (logging) |
El código de prueba que genera el agente tiende a ser una prueba tautológica que simplemente copia y pega el código de implementación. Estas pruebas no capturan en absoluto los defectos reales del negocio. Se debe aplicar estrictamente la regla: "Si ya existe una utilidad, eliminar la nueva utilidad creada por el agente y reutilizar el código existente".
Procedimiento de validación manual
- Detalle del trabajo: Rellenar la plantilla de trabajo y las puertas de CI con una lista de verificación que valide la seguridad, el rendimiento y los tipos.
- Método de ejecución:
- Incluir en la plantilla de PR los ítems de Parameterized Query, prevención de consultas N+1 y prohibición de
any.
- Vincular herramientas de análisis estático al Git Pre-commit Hook para rechazar los commits si se detectan
as any o await dentro de bucles.
- Durante la revisión, reemplazar forzosamente las utilidades creadas arbitrariamente por el agente con los módulos comunes existentes.
- Resultado esperado: Previene accidentes donde defectos como consultas N+1 o fugas de memoria pasen a producción.
3. Prompting dividido para evitar el estancamiento del pensamiento
Si se introduce una lógica compleja de liquidación o un procesamiento de pedidos basado en máquinas de estado en un solo prompt, el agente queda atrapado en un bucle de llamadas a herramientas o solo entrega código superficial. Esto se debe a que la secuencia de asignación de tokens se enreda al intentar procesar el diseño de esquemas, interfaces de API, manejo de excepciones y reglas de negocio todo a la vez.
`
[Paso 1: Modelado de datos] -> Creación de entidades de BD y esquemas Zod
│
▼ (Pasar el resultado obtenido como contexto)
[Paso 2: Definición de interfaz] -> Definición de DTOs de API, Custom Errors y firmas de servicio
│
▼ (Pasar los resultados de los Pasos 1+2 como contexto)
[Paso 3: Implementación de lógica de negocio] -> Transacciones, transiciones de estado y control de concurrencia completados
`
Intercambiar este proceso manualmente es bastante molesto. Es mejor escribir un script de automatización CLI (scripts/agent-decomposed-build.ts) para vincular la salida de la etapa anterior como contexto de entrada para la siguiente etapa.
`typescript
import { execSync } from 'child_process';
import * as fs from 'fs';
interface TaskPipeline {
featureName: string;
stage1Prompt: string;
stage2Prompt: string;
stage3Prompt: string;
}
async function runDecomposedAgentPipeline(pipeline: TaskPipeline) {
console.log([Stage 1] Executing Data Modeling for ${pipeline.featureName}...);
const stage1Output = execSync(claude --print "${pipeline.stage1Prompt}").toString();
fs.writeFileSync(./tmp/${pipeline.featureName}_stage1.ts, stage1Output);
console.log([Stage 2] Executing Interface Definition...);
const stage2InputPrompt = ${pipeline.stage2Prompt}\n\nContext Models:\n${stage1Output};
const stage2Output = execSync(claude --print "${stage2InputPrompt}").toString();
fs.writeFileSync(./tmp/${pipeline.featureName}_stage2.ts, stage2Output);
console.log([Stage 3] Executing Business Logic Implementation...);
const stage3InputPrompt = ${pipeline.stage3Prompt}\n\nContext Models:\n${stage1Output}\n\nContext Contracts:\n${stage2Output};
const stage3Output = execSync(claude --print "${stage3InputPrompt}").toString();
fs.writeFileSync(./src/services/${pipeline.featureName}.service.ts, stage3Output);
console.log([Pipeline Complete] Business logic generated cleanly without cognitive stagnation.);
}
`
Construcción del script de prompting dividido
- Detalle del trabajo: Establecer una plantilla de prompt de 3 pasos y publicar un script CLI que los ejecute de forma secuencial.
- Método de ejecución:
- Dividir los prompts en 3 partes: modelado de datos, definición de interfaces e implementación de lógica de negocio.
- Escribir
scripts/agent-decomposed-build.ts para conectar la salida anterior al contexto del siguiente prompt.
- Ejecutar este script al crear un nuevo módulo complejo.
- Resultado esperado: Desaparece el fenómeno en el que el agente se queda paralizado sin dar respuestas, y la tasa de rehacer trabajo modificando código a mano cae del rango del 20% a menos del 5%.
4. Configuración para dejar los fundamentos de las decisiones dentro del código
A medida que se usan herramientas de programación con IA, se acumula "código de solo escritura" al que le faltan las razones de por qué se estructuró de esa manera. El código donde no se registran los pros y contras de las elecciones de arquitectura se convierte en una carga difícil de manejar posteriormente por un humano. Se debe obligar en AGENTS.md a que el agente escriba comentarios estándar TSDoc dentro del código fuente al crearlo.
`typescript
/**
- @description Processes deferred settlement payouts for multi-vendor orders.
- @why Uses pessimistic database locking on the Wallet entity instead of optimistic locking because payout calculation involves high-frequency concurrent balance updates.
- @tradeoff Slight P95 latency increase under high contention in exchange for 0% financial drift.
- @complexity Time: O(N log N) due to vendor sorting | Space: O(N) for batch processing buffer.
*/
export async function processDeferredSettlement(orderId: string): Promise {
if (account.hasOutstandingBalance()) {
this.applySettlementHold(account);
}
}
`
Para evitar que las configuraciones de los agentes difieran entre desarrolladores, se debe publicar AGENTS.md como la única fuente de verdad y ejecutar un script (tools/sync-agent-rules.ts) que lo sincronice con CLAUDE.md y .cursor/rules/global.mdc al realizar un commit.
`typescript
import * as fs from 'fs';
import * as path from 'path';
const AGENTS_MD_PATH = path.join(__dirname, '../AGENTS.md');
const CLAUDE_MD_PATH = path.join(__dirname, '../CLAUDE.md');
const CURSOR_RULE_PATH = path.join(__dirname, '../.cursor/rules/global.mdc');
function syncRules() {
if (!fs.existsSync(AGENTS_MD_PATH)) {
console.error('Error: AGENTS.md does not exist.');
process.exit(1);
}
const baseRules = fs.readFileSync(AGENTS_MD_PATH, 'utf-8');
const claudeContent = # AUTOMATICALLY GENERATED FROM AGENTS.md - DO NOT EDIT DIRECTLY\n\n${baseRules};
fs.writeFileSync(CLAUDE_MD_PATH, claudeContent);
const mdcHeader = ---\ndescription: Global Agent Rule Sync\nglobs: **/*\nalwaysApply: true\n---\n\n;
fs.writeFileSync(CURSOR_RULE_PATH, ${mdcHeader}${baseRules});
console.log('Successfully synchronized AGENTS.md to CLAUDE.md and Cursor MDC rules.');
}
syncRules();
`