Tag: Claude Code

  • Claude Code Mods: un mod que no deja tocar tus tests

    Claude Code Mods: un mod que no deja tocar tus tests

    La primera versión de mi mod de Claude Code solo vigilaba Bash.

    Lo probé en Windows. Le pedí al agente que cambiara un test firmado de 409 a 201. Intentó apagar el candado y no pudo. Entonces tiró de PowerShell, la otra terminal que Claude Code tiene en Windows. Esa vez falló, pero no gracias a mí: mi mod no la vigilaba.

    Ese es el problema de fondo. Con un test en rojo, el camino más corto al verde no es arreglar el código: es cambiar lo que espera el test. Los Claude Code Mods, que llegaron el 1 de octubre de 2026, son la primera herramienta que me deja cerrar esas puertas desde dentro.

    En corto: un mod de Claude Code es un plugin con código TypeScript o JavaScript que se ejecuta dentro de Claude Code y puede observar, reescribir o responder a cada evento, como un middleware. Con menos de 80 líneas puedes bloquear cualquier Edit o Write sobre tus tests firmados y deshacer lo que el agente cambie desde la terminal. No va en sandbox y la API todavía puede cambiar entre versiones: úsalo como primera barrera, nunca como la última.

    ¿Qué son los Claude Code Mods?

    Los Claude Code Mods son plugins cuyo hooks/hooks.json tiene la clave modules apuntando a un archivo TypeScript o JavaScript que Claude Code ejecuta en su propio proceso, enganchado a eventos como una llamada a herramienta, un prompt o el dibujado de la interfaz.

    Llegaron con Claude Code v2.1.287, publicada el 1 de octubre de 2026 según el CHANGELOG oficial. Cómo funcionan está en la documentación oficial. Si tu claude --version es más antigua, no carga ninguno.

    Cada hook recibe $, la API del motor; e, el evento; y next, lo que iba a pasar. Si vienes de Express, ya lo entiendes:

    return next(e)                       // observar: que siga igual
    return next({ ...e, text: nuevo })   // reescribir: que siga, pero cambiado
    return { deny: 'No.' }               // responder (en tool.call): no llamas a next y no pasa
    

    El problema: el agente "arregla" el test

    Con un test en rojo, Claude Code tiende a cambiar la aserción en lugar de arreglar el código. No es una manía mía: en el repo de Claude Code está el issue #7074, abierto en septiembre de 2025 y cerrado como duplicado, que lo describe tal cual: el agente modifica los tests para que pasen en lugar de arreglar la implementación, cambia las aserciones y debilita validaciones. Que sea un duplicado dice bastante.

    En mi demo, una API de reservas en Bun, los tests de test/contrato/ son los que he revisado y firmado. Uno dice que si dos personas reservan el mismo hueco, una recibe 201 y la otra 409. El código falla a propósito.

    El agente puede tocar ese archivo por tres puertas: Edit, Write y la terminal (sed, echo > o PowerShell). Escribir "no toques los tests" en el CLAUDE.md no cierra ninguna: es una petición, no un control. Lo conté en hooks vs permisos en Claude Code.

    Cómo crear un mod de Claude Code paso a paso

    Crear un mod de Claude Code son tres archivos: el manifiesto del plugin, un hooks.json con modules y el módulo que exporta register(on). Sin compilar ni bundler.

    El mod vive fuera del proyecto, en su carpeta. Primero, .claude-plugin/plugin.json (el nombre no puede empezar por claude-):

    {
      "name": "tests-lock",
      "version": "0.1.0",
      "description": "Stops Claude from editing the signed tests in test/contrato/ and warns when a shell command changes them",
      "author": { "name": "Dominicode" }
    }
    

    Lo que convierte este plugin en un mod está en hooks/hooks.json:

    {
      "description": "tests-lock hooks module",
      "modules": ["./register.ts"]
    }
    

    Primera puerta: Edit y Write

    const PROTECTED = 'test/contrato/'
    const REASON =
      'test/contrato/ es el contrato firmado y no se cambia para que pase. ' +
      'Arregla el código, no el test. Si crees que el contrato está mal, para y pregúntame.'
    
    export function register(on) {
      // 1. Edit y Write: bloquear antes de que se toque un test firmado
      on('tool.call', { tool: ['Edit', 'Write'] }, async ($, e, next) => {
        if (!locked || !isProtected(e.file_path)) return next(e)
        // Crear un test nuevo sí se permite; cambiar uno que ya existe, no
        if (!(await $.fs.exists(e.file_path))) return next(e)
        // ...contador y aviso en pantalla
        return { deny: REASON }
      }).catch(async () => ({ deny: 'tests-lock ha fallado y, por seguridad, no se edita nada en ' + PROTECTED }))
    }
    

    El agente lee el deny como el error de la herramienta, así que lo escribo como instrucción. Crear un test nuevo sí se permite. Es un extracto: locked, isProtected y el resto están en el código completo, al final del post. Y el .catch hace que falle cerrado: si el hook revienta, la respuesta es "no". Sin él, Claude Code se salta el hook y la edición pasa.

    Segunda puerta: la terminal

    Esto es lo que justifica el mod. No sé de antemano qué toca un comando, y adivinarlo con regex es perder. Así que compruebo: miro git antes, dejo correr el comando y miro git después.

    // Terminal (Bash, y PowerShell en Windows): se mira git antes y después
    on('tool.call', { tool: ['Bash', 'PowerShell'] }, async ($, e, next) => {
      if (!locked) return next(e)
      const before = await changedTests($)
      const result = await next(e)
      const touched = (await changedTests($)).filter((f) => !before.includes(f))
      if (touched.length === 0) return result
      // Solo se restauran los que estaban limpios antes del comando
      await $.process.run(['git', 'checkout', 'HEAD', '--', ...touched])
      return {
        ...result,
        context: [...(result.context ?? []), 'Tu último comando cambió ' + touched.join(', ') + ' y se ha deshecho. ' + REASON],
      }
    })
    

    changedTests lanza git diff --name-only HEAD -- test/contrato/ con $.process.run, sin shell. El await next(e) ejecuta el comando y mi código sigue después.

    context es texto que el agente lee tras el resultado y tú no ves. En mis pruebas, el agente citó el aviso y el archivo volvió a esperar 409. Fíjate en 'PowerShell': la añadí después de la historia del principio. Con Claude Code Mods, cada herramienta que no nombras es una puerta abierta.

    Para cargarlo: claude --plugin-dir ~/mods/tests-lock. En /plugin lo ves cargado, y al guardar se recarga en caliente.

    Un mod también pinta. Con ui.render sobre AbovePrompt dibujas una franja encima del prompt; por ejemplo, con el último resultado de bun test, Vitest o Jest.

    El estado que lee esa franja vive en atom, read y update de 'claude-code'. Un hook de settings no puede dibujar.

    Probar Claude Code Mods: claude plugin validate y claude plugin test

    Un mod es código que corre dentro de tu herramienta de trabajo, así que se prueba como cualquier código.

    claude plugin validate . lee el mod sin ejecutarlo y lista eventos y llamadas. Salida real:

    ❯ ./register.ts hooks: session.start, tool.call{tool=Edit|Write}, tool.call{tool=Bash|PowerShell}, command.run{command=tests-lock}
    ❯ ./register.ts calls: $.command.register, $.fs.exists, $.process.run, $.ui.status (via showStatus), $.ui.toast
    ✔ Validation passed
    

    claude plugin test corre los *.test.ts con 'claude-code/testing', sin sesión ni red. Cada on es un stub que responde en lugar de Claude Code:

    import { expect, test } from 'claude-code/testing'
    
    test('editar un test firmado se bloquea', async ($, on) => {
      on('ui.status', () => ({ value: undefined }))
      on('ui.toast', () => ({ value: undefined }))
      on('fs.exists', () => ({ value: true }))
      on('tool.call', () => ({ result: 'edited' }))
    
      const r = await $.tool.call({ tool: 'Edit', file_path: 'C:\\proyectos\\agendo\\test\\contrato\\reservas.test.ts', old_string: '409', new_string: '201' })
      expect(r.deny).toMatch(/contrato firmado/)
    })
    

    Cinco tests en verde: bloqueo, test nuevo, código normal, comando de Bash deshecho e interruptor /tests-lock off.

    Claude Code mods vs hooks de settings: cuál usar

    Un hook de settings es un script que Claude Code lanza desde fuera y vive en el repo; un mod corre dentro de Claude Code, guarda estado, puede dibujar y actuar después de que la herramienta termine.

    Hook de settings.json Mod
    Dónde vive En .claude/settings.json, versionado en git En un plugin que cada dev instala
    Qué escribes Un script en cualquier lenguaje TypeScript o JavaScript
    Estado entre llamadas No, salvo un archivo temporal Sí, variables del módulo
    Actuar tras la herramienta Con un segundo hook (PostToolUse) En el mismo hook, tras await next(e)
    Interfaz y comandos propios No Sí
    Tests Los que montes tú claude plugin test sin sesión
    Limitación o riesgo Repartir estado entre dos scripts es frágil Sin sandbox, API que aún puede cambiar entre versiones, instalación por máquina

    Mi regla: si todo el equipo tiene que cumplirla sin instalar nada, hook de settings en el repo. Si es tu herramienta y necesita estado, interfaz o mirar después del comando, mod. Lo de la terminal también sale con PreToolUse, PostToolUse y un archivo temporal; el mod lo junta en un archivo con tests.

    Cuándo NO usar Claude Code Mods

    No uses Claude Code Mods como única barrera ni instales un mod que no hayas leído.

    No va en sandbox. La documentación lo dice: corre con tus permisos, lee tus variables de entorno y claves, ve cada prompt y puede aprobar llamadas que un hook tuyo bloqueó.

    El sandbox de Claude Code no cubre lo que lanza un mod. Antes de instalar uno, claude plugin validate y lee los calls. Si algo va raro, claude --safe-mode arranca sin tus mods.

    La API todavía puede cambiar. La documentación da los mods por activados por defecto desde la v2.1.287, pero la cabecera de los tipos avisa: "this surface may change between releases without notice".

    Compara con el último commit. Lo no commiteado no cuenta como firmado. Y por cómo está escrito, si un mismo comando cambia el test y hace commit, el git diff posterior sale limpio. Lo deduzco del código; aún no lo he demostrado en sesión real.

    Al recargar, el estado se reinicia. El contador vuelve a cero y el candado se enciende.

    Solo protege donde está instalado. Otro compañero o el CI no lo tienen. La última barrera es el CI ejecutando los tests firmados en cada PR. Es la lógica de sacar la regla fuera del prompt: el agente propone, el sistema controla. Si aún no has montado tu primer agente, empieza por construir un agente de IA desde cero.

    Lo que puedes hacer hoy con tu primer mod

    Para empezar con Claude Code Mods: copia el register.ts completo del final del post, cambia test/contrato/ por tu carpeta, commitea los tests y carga el mod con --plugin-dir. Luego pide al agente que cambie una aserción, por Edit y por la terminal. Si estás en Windows y solo vigilas Bash, ya sabes por dónde se cuela.

    Si estás empezando con agentes y quieres hacerlo sin perder el control, tienes gratis el ebook El Developer Agéntico. Lo monto entero en el canal de YouTube de Dominicode. Para el flujo completo, está el curso Construye con IA. Y los tests que merece la pena firmar salen de una spec: lo cuento en Spec-Driven Development.

    Preguntas frecuentes

    ¿Qué versión de Claude Code necesito para usar mods?

    La v2.1.287 o posterior, publicada el 1 de octubre de 2026. Compruébala con claude --version. Vienen activados por defecto y se cargan desde un plugin instalado o con --plugin-dir en desarrollo.

    ¿En qué se diferencian los hooks de Claude Code de los mods?

    Un hook de settings es un script externo configurado en settings.json que recibe un JSON y devuelve una decisión. Un mod corre dentro de Claude Code, comparte variables entre hooks, puede dibujar, registrar comandos y actuar después de que una herramienta termine.

    ¿Un plugin de Claude Code con mod es seguro de instalar?

    Solo si confías en quien lo escribió. No va en sandbox y corre con tus permisos. Antes de instalarlo, ejecuta claude plugin validate sobre su carpeta y revisa los hooks y calls que lista.

    ¿Puede el agente saltarse el mod usando la terminal?

    Si el mod solo vigila Edit y Write, sí. Este mira git antes y después de cada comando de Bash y PowerShell y restaura los tests firmados que cambien. En Windows hay que vigilar las dos herramientas. Con un límite: si el mismo comando cambia el test y hace commit, el diff sale limpio. Por eso el CI es la última barrera.

    ¿Puedo crear un mod de Claude Code sin escribir el código?

    Sí. La documentación oficial propone pedírselo a Claude en una sesión: Claude Code trae una skill para escribir mods. Revisa el resultado con claude plugin validate antes de cargarlo.

    Código completo del mod

    hooks/register.ts de tests-lock, las 78 líneas tal cual las uso:

    // tests-lock: lo firmado en test/contrato/ no se toca para que pase.
    const PROTECTED = 'test/contrato/'
    const REASON =
      'test/contrato/ es el contrato firmado y no se cambia para que pase. ' +
      'Arregla el código, no el test. Si crees que el contrato está mal, para y pregúntame.'
    
    let locked = true
    let blocked = 0
    
    function isProtected(path: string): boolean {
      return ('/' + path.replaceAll('\\', '/')).includes('/' + PROTECTED)
    }
    
    // Archivos de test/contrato/ que hoy no coinciden con el último commit
    async function changedTests($): Promise<string[]> {
      try {
        const r = await $.process.run(['git', 'diff', '--name-only', 'HEAD', '--', PROTECTED])
        if (r.exitCode !== 0) return []
        return r.stdout.split('\n').map((l) => l.trim()).filter(Boolean)
      } catch {
        return []
      }
    }
    
    function showStatus($) {
      $.ui.status(locked ? `${PROTECTED} protegido · ${blocked} intento(s) frenado(s)` : `${PROTECTED} SIN proteger`)
    }
    
    export function register(on) {
      on('session.start', async ($, e, next) => {
        showStatus($)
        await $.command.register({
          name: 'tests-lock',
          description: 'Protege test/contrato/: on, off o status',
          argumentHint: '[on|off|status]',
          immediate: true,
        })
        return next(e)
      })
    
      // 1. Edit y Write: bloquear antes de que se toque un test firmado
      on('tool.call', { tool: ['Edit', 'Write'] }, async ($, e, next) => {
        if (!locked || !isProtected(e.file_path)) return next(e)
        // Crear un test nuevo sí se permite; cambiar uno que ya existe, no
        if (!(await $.fs.exists(e.file_path))) return next(e)
        blocked += 1
        showStatus($)
        $.ui.toast('Claude ha intentado editar ' + e.file_path)
        return { deny: REASON }
      }).catch(async () => ({ deny: 'tests-lock ha fallado y, por seguridad, no se edita nada en ' + PROTECTED }))
    
      // 2. Terminal (Bash, y PowerShell en Windows): no se sabe de antemano qué toca un comando,
      //    así que se mira git antes y después
      on('tool.call', { tool: ['Bash', 'PowerShell'] }, async ($, e, next) => {
        if (!locked) return next(e)
        const before = await changedTests($)
        const result = await next(e)
        const touched = (await changedTests($)).filter((f) => !before.includes(f))
        if (touched.length === 0) return result
        // Solo se restauran los que estaban limpios antes del comando
        await $.process.run(['git', 'checkout', 'HEAD', '--', ...touched])
        blocked += 1
        showStatus($)
        $.ui.toast('Un comando ha cambiado ' + touched.join(', ') + '. Restaurado.')
        return {
          ...result,
          context: [...(result.context ?? []), 'Tu último comando cambió ' + touched.join(', ') + ' y se ha deshecho. ' + REASON],
        }
      })
    
      on('command.run', { command: 'tests-lock' }, async ($, e) => {
        const arg = e.args.trim()
        if (arg === 'on') locked = true
        if (arg === 'off') locked = false
        showStatus($)
        return { text: (locked ? 'Protegido: ' : 'Sin proteger: ') + PROTECTED + ' · intentos frenados: ' + blocked }
      })
    }
    

    Por Bezael Pérez — Developer senior con más de 15 años de experiencia y fundador de Dominicode.

  • Cómo calculo cuánto me cuesta de verdad un agente que falla

    Cómo calculo cuánto me cuesta de verdad un agente que falla

    El miércoles que casi aprobé el email con el precio desactualizado —lo monté un agente en MailerLite con el precio de la semana anterior, listo para salir a toda la lista— se lo conté a un colega dos días después.

    Me preguntó algo que no supe responder bien: "¿por qué revisaste ese y los cuarenta y dos thumbnails de la noche anterior los aprobaste sin mirar ni uno?". Dije "depende del riesgo". Se quedó esperando un número. No lo tenía.

    "Depende" no es un criterio, es decidir a ojo. Y a ojo, tarde o temprano, fallas por el lado que más duele.

    Me senté a calcular, en números y no en corazonadas, cuánto cuesta un agente de IA que falla. La misma cuenta que hago hoy antes de aprobar lo que entrega cualquier agente.

    En corto: para decidir si reviso lo que entrega un agente de IA antes de aprobarlo, comparo dos números: el coste esperado de que falle sin que yo lo vea (probabilidad × daño) contra el coste de verificarlo yo mismo. Si el primero es mayor, reviso siempre. Si es menor, delegar sin mirar es la decisión más barata — no la más cómoda.

    ¿Qué es el coste esperado de no verificar una tarea delegada?

    El coste esperado de no verificar es la probabilidad de que la tarea delegada falle, multiplicada por todo lo que te cuesta cuando falla: detectarlo tarde, arreglarlo y el daño que ya hizo antes de que te dieras cuenta.

    El coste de verificar es más simple: es el tiempo que te cuesta a ti —o a quien revise— leer, entender y aprobar esa tarea antes de que se vuelva irreversible.

    La regla de decisión sale sola al poner los dos números uno al lado del otro. Si el coste esperado de no verificar es mayor que el coste de verificar, revisas siempre. Si es menor, delegas sin revisión — no por pereza, sino porque es la opción matemáticamente más barata.

    Este principio —revisar antes de que algo se vuelva irreversible— es el corazón del método que dejé completo y gratis en el ebook Revisión por Contrato: cómo definir de antemano qué necesita aprobación humana y qué no.

    Por qué no tomo prestado el "100x más caro en producción" de la industria

    Antes de construir esta fórmula tuve la tentación de usar el número que todo el mundo repite: que un bug en producción cuesta cien veces más arreglarlo que uno detectado en desarrollo. Aparece en charlas, en posts de blog, en pitch decks de herramientas de testing.

    Ese número, rastreado hasta la fuente, puede que no exista tal como se cita. Un hilo de Hacker News de 2021, con 159 puntos y 130 comentarios —"The 'bugs are 100x more expensive to fix in production' study might not exist"— sigue la cadena de citas hasta el estudio original y no encuentra una medición limpia detrás, solo cita tras cita.

    El usuario nerdponx lo resume mejor de lo que yo podría: "No es un caso de investigación falsificada. Es un caso de alguien citando algo apócrifo como si fuera un hecho, y luego otra gente citando esa cita" —traducido del inglés.

    Por eso, abajo, no uso ningún multiplicador prestado de la industria. Los números de probabilidad y de coste son míos, ilustrativos, pensados para mostrar el mecanismo — no una estadística que puedas citar como un dato medido.

    El cálculo real: el email con el precio equivocado

    Uso $50 la hora como referencia ilustrativa de mi propio tiempo — no es una tarifa de mercado. Cambia el número por el tuyo: el mecanismo de la fórmula no varía.

    Releer el asunto y el precio antes de aprobar el envío me costó entre 2 y 3 minutos: unos $2,50. Ese es el coste de verificar.

    La probabilidad de que el precio hubiera cambiado justo esa semana, sin que yo lo notara, era baja — pongamos, ilustrativamente, un 5%.

    Pero si fallaba, el coste no era pequeño. Arreglarlo —corrección más responder a quien preguntara— son unas 2 horas: $100. Y está el daño ya hecho antes de detectarlo: la credibilidad del precio frente a miles de bandejas que ya leyeron una cifra falsa. No tiene cifra exacta, pero tiene un piso conservador — ilustrativamente, $200. Total si falla: ≈ $300.

    Coste esperado de no verificar = 5% × $300 = $15.

    $15 es mayor que $2,50. La fórmula dice: verifica siempre. Y lo que gana la decisión no es la probabilidad —era baja— sino la magnitud del daño si el evento raro ocurre.

    El contraste: 42 thumbnails, probabilidad alta, coste casi cero

    La noche anterior había dejado al mismo agente generando cuarenta y dos thumbnails para posts antiguos: prompt, imagen, nombre de archivo, carpeta correcta. Los aprobé todos sin abrir ni una carpeta.

    Aquí la probabilidad de que algo saliera mal era, ilustrativamente, alta — un 30%: con cuarenta y dos piezas generadas en patrón, algo se cuela con frecuencia.

    Pero si falla, el coste es casi nada. Se ve al abrir la carpeta —dos minutos, unos $1,70— y se regenera con un comando. No hay corrección pública ni daño reputacional: nadie fuera de mí ve el error antes de que lo arregle.

    Coste esperado de no verificar = 30% × $1,70 ≈ $0,51.

    Verificar las 42 imágenes una por una, a un minuto cada una, cuesta $35. $0,51 es muchísimo menor que $35. La fórmula dice: delega sin revisar. Aquí la probabilidad alta no importa, porque el daño y la irreversibilidad son casi cero.

    Elemento del cálculo Email de lanzamiento 42 thumbnails
    Coste de verificar antes de aprobar 3 min ≈ $2,50 42 min (1 min c/u) ≈ $35
    Probabilidad ilustrativa de que falle 5% — evento infrecuente 30% — patrón repetido, muchas piezas
    Coste si falla (arreglar + daño ya hecho) ≈ $300 (corrección + credibilidad) ≈ $1,70 (se regenera con un comando)
    Coste esperado de NO verificar (P × coste si falla) ≈ $15 ≈ $0,51
    Qué inclina la balanza La magnitud del daño, no la probabilidad Lo barato y rápido que es detectar y arreglar
    Decisión de la fórmula Verificar siempre Delegar sin revisar

    La fila que importa es la penúltima. No decide la probabilidad: decide qué tan caro sale el daño si el evento raro ocurre, y qué tan barato es deshacerlo si no.

    Es el mismo cálculo que aplico al diseñar los agentes que uso a diario, y es lo que enseño paso a paso en Construye con IA: no solo montar el agente, también decidir qué parte de su trabajo se aprueba sin mirar y cuál se revisa siempre.

    Si esto te suena al criterio de blast radius que ya usaba antes de tener la fórmula, es porque es el mismo criterio con números detrás. Para el ángulo más amplio de por qué creo que el techo de los agentic systems es económico y no técnico, lo desarrollé en este otro post.

    Cuándo esta fórmula no sirve

    No es una máquina de la verdad. Tiene límites que conviene decir en voz alta antes de que alguien la use como excusa para no pensar.

    Estimar la probabilidad es subjetivo, y es fácil engañarte a ti mismo minimizándola. Si el agente acertó las últimas diez veces, tu cerebro te dirá que la probabilidad de fallo es más baja de lo real. Ese sesgo no lo corrige la fórmula — el número lo pones tú, con tu sesgo incluido.

    El coste del daño reputacional no tiene un número exacto. La fórmula te obliga a poner una cifra, pero esa cifra es una estimación conservadora, no una medición. Si la subestimas para que el cálculo te dé el resultado que ya querías, el cálculo miente a tu favor.

    Esta fórmula evalúa una tarea aislada, no un sistema. No sustituye un gate automático —tests, tipos, criterios de aceptación que corran solos— que verifique sin depender de que te acuerdes de hacer la cuenta cada vez. Es la capa manual para cuando ese gate todavía no existe.

    Qué hacer hoy con esto

    No necesitas una hoja de cálculo. Coge las tres tareas que más delegas esta semana a un agente y, para cada una:

    1. Escribe cuánto te cuesta verificarla antes de aprobarla.
    2. Ponle una probabilidad ilustrativa a que falle.
    3. Estima cuánto costaría si falla —arreglo más daño ya hecho antes de detectarlo.
    4. Multiplica los puntos 2 y 3: ese es tu coste esperado de no verificar.

    Compara ese resultado con el coste de verificar del punto 1. Donde gane verificar, sigue revisando sin culpa —no es desconfianza en el agente, es aritmética—. Donde pierda, suelta esa tarea del todo.

    Si quieres ver cómo aplico esta cuenta cada semana en un negocio real que opero solo, sin nadie más que revise detrás de mí, en Dominicode Labs comparto los números actualizados y las tareas concretas que delego o audito según van cambiando mis agentes.


    Preguntas frecuentes

    ¿Cómo calculo cuánto me cuesta que un agente de IA falle?

    Multiplica la probabilidad de que la tarea falle por el coste total si falla: detectarlo tarde, arreglarlo y el daño ya hecho antes de que lo notes. Ese resultado es el coste esperado de no verificar. Compáralo con el coste de verificar antes de aprobar — el que sea menor gana.

    ¿Qué diferencia hay entre esta fórmula y el criterio de blast radius?

    Ninguna en el fondo — son el mismo criterio en dos niveles. El blast radius es la versión cualitativa: cuánto daño hace algo y qué tan reversible es. Esta fórmula es la versión cuantitativa: le pones números a ese daño y a esa probabilidad para comparar dos tareas objetivamente, no a ojo.

    ¿Cómo estimo la probabilidad de que una tarea delegada a un agente falle?

    Con honestidad, sabiendo que es una estimación y no una medición. Fíjate en cuántas veces has visto fallar ese tipo de tarea, cuánto contexto de negocio necesita que pueda haber cambiado sin que el agente lo sepa, y desconfía de tu propia racha reciente de aciertos.

    ¿Esta fórmula sustituye tener tests automáticos o un gate de verificación?

    No. Decide sobre una tarea puntual, cuando no existe todavía un mecanismo automático que verifique el resultado por ti. Si puedes construir ese gate —tests, tipos, criterios de aceptación que corran solos— constrúyelo: es más fiable que cualquier cálculo manual que dependa de que te acuerdes de hacerlo cada vez.

    ¿Qué hago si no puedo poner un número exacto al daño reputacional?

    Pon un piso conservador y dilo explícitamente: es una estimación, no una medición. El objetivo no es acertar la cifra exacta, sino evitar el error más común, que es tratar el daño reputacional como si costara cero solo porque no tiene un precio de catálogo.


    Por Bezael Pérez — Developer senior con más de 15 años de experiencia y fundador de Dominicode.

  • Usar IA para programar: los 4 errores de mis primeros 3 meses

    Usar IA para programar: los 4 errores de mis primeros 3 meses

    Hace tres meses, en junio de 2026, hice merge de un endpoint generado por ChatGPT dentro de Kursar sin leerlo línea por línea. Compilaba. Los dos tests que traía pasaban — los había escrito la misma IA que escribió el endpoint. Lo aprobé un jueves a las diez de la noche porque tenía prisa.

    Quince días después, ese endpoint dejó pasar un payload que no debía. Tuve que revertir un commit en producción a las once de la noche sin saber qué línea lo había roto — nunca la había leído.

    Ese fue el momento en que entendí que empezar a usar IA para programar no es un problema de qué herramienta eliges primero. Es un problema de qué hábitos construyes en las primeras semanas. Y yo construí los equivocados.

    En corto: en mis primeros tres meses usando IA para programar cometí cuatro errores caros: chat web como herramienta principal, prompts sin contrato, código sin revisar línea por línea, y demasiadas herramientas probadas a la vez. Si empezara hoy, instalaría una sola herramienta agéntica, escribiría el contexto antes que el prompt, y no aprobaría nada que no hubiera leído yo mismo.

    ¿Qué es revisar código por contrato?

    Revisar código por contrato es comprobar que lo que generó la IA cumple una lista explícita de condiciones —qué debe hacer, qué no debe romper, con qué se valida— antes de aceptarlo. No es leer por encima para ver si "se ve bien": es fijar el contrato antes de escribir el prompt, no después de leer la respuesta. Ya escribí sobre este método completo en Revisar código generado por IA: el método Revisión por Contrato; aquí va la versión resumida de por qué me costó tan caro no aplicarlo desde el día uno.

    En el mes uno yo no hacía esto. Escribía un prompt sin contrato y aprobaba lo que volviera si compilaba:

    // Mes uno: sin contrato
    "Mejora este endpoint de pagos"
    
    // Ahora: con contrato
    "Modifica solo el endpoint POST /payments.
    No cambies la firma de la función ni el schema de respuesta.
    El monto debe seguir validándose con Zod antes de llamar al proveedor.
    Si el proveedor devuelve error, reintenta máximo 2 veces con backoff."
    

    El contrato lo inventé después de romper algo en producción, que es la forma más cara de aprenderlo.

    Los cuatro errores que más me costaron

    No los cometí por descuido. Los cometí porque nadie me dijo que el problema no era la herramienta, sino el orden en que construía el hábito de usarla.

    Hábito Cómo empecé (mes 1) Qué cambié Coste que me hubiera ahorrado
    Herramienta principal Chat web genérico (ChatGPT), copiar y pegar código a mano CLI agéntica que lee el repo completo (Claude Code) Semanas reescribiendo contexto a mano en cada prompt
    Cómo pedía las cosas "Mejora este código", "arregla este bug" Prompt con contrato: qué debe cumplir, qué no debe tocar, con qué se valida Una noche entera revirtiendo un commit que rompió un flujo en producción
    Revisión antes de aceptar Merge si compilaba y pasaban los tests que la propia IA había escrito Diff línea por línea + criterios de aceptación que escribo yo antes de pedir el código El bug de producción del endpoint que abre este post
    Herramientas probadas Cinco en paralelo la primera semana (Copilot, Cursor, ChatGPT, Claude web, Codeium) Una sola herramienta, mínimo dos o tres semanas antes de evaluar otra Casi un mes sin dominar ninguna a fondo

    No soy el único al que le pasó esto. En el hilo de Hacker News "The AI coding trap" hay un comentario que lo resume mejor que yo: "if you yolo your way through a build without thought, it will collapse". Otro añade algo que se aplica directo a mi endpoint: la deuda técnica generada por código de IA sin revisar "isn't paid down, it's being added to".

    Eso es exactamente lo que pasó con mis dos primeros meses: no estaba pagando deuda, la estaba acumulando cada vez que aprobaba un diff sin leerlo.

    La ruta que seguiría si empezara hoy

    Si tuviera que borrar los tres meses y empezar de nuevo, este es el orden exacto, no una lista de buenas intenciones:

    1. Instala una herramienta agéntica de terminal antes que una extensión de autocompletado. Necesitas ver cómo razona sobre el repo completo, no solo qué te autocompleta línea a línea. A mí lo que me cambió el flujo fue Claude Code — en el curso Construye con IA parto de cero con esta misma herramienta, sin dar por hecho nada.
    2. Escribe el contexto antes que el prompt. Qué archivos puede tocar, qué no debe romper, con qué criterio se valida. Un prompt sin contrato produce una solución genérica para un problema que no era genérico.
    3. No apruebes un diff sin leerlo, ni una sola vez, en las primeras semanas. Es el hábito más caro de perder y el más barato de mantener desde el día uno.
    4. Escribe tú los criterios de aceptación antes de pedir el código. No dejes que la misma IA que escribió la función te diga si la función está bien — ese fue mi error con los tests del endpoint. Este marco lo dejé completo en Revisar código generado por IA: el método Revisión por Contrato, justo para no repetir mi error.
    5. Comprométete con una sola herramienta dos o tres semanas antes de evaluar otra. Cambiar cada dos días es la forma más cara de no aprender ninguna a fondo.

    Lo que la IA todavía no te resuelve

    Nada de esto convierte a la IA en un sustituto de tu criterio. Dos límites reales, no teóricos, con los que me sigo topando:

    • No conoce las restricciones de negocio que nadie escribió en ningún sitio. La decisión de arquitectura que tomaste hace dos años por una razón que ya nadie recuerda. Te va a dar una solución "correcta" en el vacío, y ese vacío es exactamente donde vive la mayoría de los bugs de producción.
    • No sustituye la revisión de seguridad. Secretos hardcodeados, dependencias inseguras, patrones peligrosos como eval o deserialización sin validar pasan la revisión superficial precisamente porque el código generado se ve profesional — y el código que se ve profesional es el que menos se revisa a fondo.

    Si tu flujo de trabajo depende de que la IA nunca se equivoque, no tienes un flujo de trabajo. Tienes una apuesta.

    Qué haría hoy, literalmente

    Si hoy tuviera que empezar de cero, esto es lo que haría antes de escribir una sola línea de código con ayuda de IA: instalar una sola herramienta agéntica, escoger una tarea pequeña y real de mi propio repo, escribir el contrato antes del prompt, y leer el diff completo antes de aprobar nada.

    No es una lista de deseos. Es lo que hago ahora, después de pagar el precio de no hacerlo en el mes uno.

    Si prefieres no reconstruir esto a partir de tus propios errores, en Construye con IA parto contigo de la idea al producto con Claude Code, con estos mismos hábitos desde la primera clase. Si ya tienes el hábito y quieres dar el siguiente paso, construir un agente de IA desde cero es la ruta lógica. Y si prefieres tener con quién comentar los errores mientras los cometes, en Dominicode Labs compartimos esto cada semana con gente que está exactamente en este punto.

    Preguntas frecuentes

    ¿Por dónde empezar a usar IA para programar si nunca lo he hecho?

    Empieza por una sola herramienta agéntica de terminal, no por el chat web. Elige una tarea pequeña y real de un proyecto que ya conozcas —no un tutorial de juguete— y practica el hábito de escribir el contrato antes del prompt y leer el diff completo antes de aprobar nada.

    ¿Qué herramienta de IA debería instalar primero?

    Depende de si trabajas sobre todo desde la terminal o desde el editor, pero para ver el repo completo y razonar sobre varios archivos a la vez, una CLI agéntica como Claude Code te enseña el hábito correcto desde el primer día. El chat web genérico está bien para preguntas puntuales, pero no para tu flujo de trabajo principal.

    ¿Es seguro dejar que la IA escriba código en producción?

    Es seguro si tú revisas cada diff con criterios definidos antes de aprobarlo, y no lo es si el criterio es "compiló" o "los tests pasaron" cuando esos tests también los escribió la IA. El riesgo no está en usar IA, está en saltarte la revisión por prisa.

    ¿Cuánto tiempo se tarda en tener un flujo de trabajo sólido con IA?

    A mí me tomó tres meses y un incidente en producción para dejar de improvisar. Si defines el contrato desde el primer día y te comprometes con una sola herramienta en vez de probar cinco a la vez, puedes llegar a un flujo sólido en dos o tres semanas.

    ¿Vale la pena seguir usando el chat web en vez de una herramienta agéntica?

    Para preguntas sueltas o para pensar en voz alta sobre un problema, sí. Para escribir código que vas a mergear en un repo real, no: pierdes el contexto del proyecto en cada mensaje y terminas pegando código a mano, que fue exactamente mi primer error.


    Por Bezael Pérez — Developer senior con más de 15 años de experiencia y fundador de Dominicode.

  • Verificar código generado por IA: 112 posts con el schema roto

    Verificar código generado por IA: 112 posts con el schema roto

    El 10 de septiembre de 2026 le pedí a un script que auditara la FAQ de todo el blog de Dominicode. No esperaba encontrar gran cosa: llevo meses aprobando cada post yo mismo antes de publicarlo, y la sección de preguntas frecuentes siempre se veía perfecta — pregunta en negrita, respuesta debajo, todo alineado en el editor de WordPress y en el navegador.

    El script devolvió 112 posts con el schema FAQPage roto.

    Es el mismo problema que tienes al intentar verificar código generado por IA con solo leer el resultado: se ve perfecto y sigue roto.

    No roto a medias. En un grupo de esos 112, el JSON-LD que se genera para Google y para cualquier motor que lea structured data tenía las preguntas literalmente llamadas "Respuesta:". Ciento doce posts publicados, revisados por mí uno por uno antes de publicarlos, y ninguno cumplía el contrato real que el frontend del blog necesita para generar ese schema.

    Nadie lo había visto leyendo el HTML. Yo tampoco.

    En corto: leer el diff o el HTML de código generado por IA no es lo mismo que verificar que cumple el contrato que otro sistema necesita para consumirlo — solo confirma que "se ve bien". Verificar código generado por IA por contrato significa ejecutar el mismo parser o extractor que usará el consumidor final antes de dar el visto bueno. Lo descubrimos auditando nuestro propio blog: 112 posts aprobados a simple vista tenían el schema FAQPage roto, invisible en el navegador, durante meses.

    ¿Qué es "verificar por contrato" y por qué no es lo mismo que leer el código?

    Verificar por contrato es comprobar que un cambio produce exactamente lo que el sistema que lo consume necesita — no que "se vea bien" para un humano que lo lee. Leer un diff o un post publicado confirma que el resultado es legible. No confirma que un parser o un test automatizado pueda procesarlo.

    Son dos preguntas distintas. "¿Se ve bien?" la responde cualquiera en cinco segundos. "¿Cumple el contrato de quien lo consume?" solo la responde ejecutar ese sistema —o replicar su lógica exacta— contra el resultado.

    Ya escribí el método completo, con el AGENTS.md entero, en Revisión por Contrato: cómo verificar código de agentes de IA. Este post es la prueba de que el método no es teoría: es lo que evitó que 112 posts siguieran rotos indefinidamente.

    El blog no usa ningún plugin de WordPress para generar el schema FAQPage. Lo genera el frontend en Next.js parseando el HTML del post con una función propia (extractFaqsFromContent, en app/lib/api.ts).

    Su contrato es estricto: la sección FAQ debe abrir con un <h2> que case con "FAQ" o "Preguntas frecuentes", cerrar en el primer <hr> o el siguiente <h2> —lo que llegue antes— y dentro de esa sección solo reconoce <h3> como pregunta y <p> como respuesta. Cualquier otra estructura, por bien que se vea en pantalla, no existe para ese parser.

    Seis formas de "verse bien" que rompían el contrato

    El catálogo acumuló seis formas distintas de escribir la FAQ, heredadas de plantillas de distintas épocas. Ninguna usaba <h3> + <p> dentro de la sección — todas se veían impecables en WordPress.

    Forma heredada Dónde vivía el id del ancla Qué producía el schema real
    A — <li> con enlace + <div id><p><strong>Respuesta:</strong> R</p></div> La respuesta Todas las preguntas literalmente "Respuesta:"
    B — igual que A, pero <strong>P</strong> cuelga del <div>, fuera del <p> La respuesta 0 preguntas — el extractor solo mira dentro de <p>
    C — <div class="faq-question"> con el enlace + <div id> con texto suelto La respuesta 0 preguntas — no hay ni <h3> ni <p>
    D — <p><a href="#id">P</a></p> como pregunta La respuesta 0 preguntas — se lee como párrafo suelto y se descarta
    E — <h3 id="id">P</h3> suelto, sin enlace de índice La propia pregunta La respuesta vive en un <div> sin <p>; la pregunta se pierde sin dejar rastro
    F — <section id="id"> que solo envuelve al <h3> La propia pregunta Mismo problema que E: el <div> de respuesta queda fuera de lo que ve el extractor

    Seis formas, un solo fallo compartido: la pregunta y la respuesta nunca vivían dentro de <h3> + <p> a la vez. El contrato no pedía nada exótico — pedía dos etiquetas concretas, en el lugar concreto.

    Por qué nadie lo vio en 112 revisiones

    Esto no es negligencia mía en particular. Es lo que le pasa a cualquier revisión manual cuando el criterio de "correcto" no es visual.

    En un hilo de Hacker News sobre por qué las code reviews casi nunca encuentran bugs, un comentario cita un dato de Wikipedia: menos del 15% de los comentarios que se dejan en una revisión de código señalan errores reales. El resto es estilo y preferencia —lo que un humano sí evalúa mirando.

    Una FAQ con formato bonito no activa ninguna alarma en un revisor humano. Activa un montón en un parser que busca <h3> y no encuentra ninguno.

    Hay un detalle que le añade ironía al caso: Google dejó de mostrar el rich snippet de FAQ en el buscador el 7 de mayo de 2026, cuatro meses antes de que reparáramos el nuestro, y sin anunciarlo —solo lo cambió en la documentación.

    ¿Reparar un schema que ya no produce un desplegable en el SERP es tiempo perdido? No: el FAQPage sigue siendo structured data válida, y sigue siendo el tipo de dato limpio y extraíble que un motor generativo necesita para leer y citar tu contenido sin tener que adivinar dónde empieza cada respuesta.

    Que Google apagara el escaparate visual no cambia que el contrato de fondo siga decidiendo si tu contenido es citable.

    Cómo se reparó — sin confiar en que "debería funcionar"

    El script scripts/fix-faq-schema.mjs corre por post individual o con --all, en modo dry-run por defecto — solo escribe con --apply explícito. Antes de tocar nada, replica el contrato exacto del extractor del frontend para diagnosticar si un post está roto. Después de reparar, vuelve a correr ese mismo contrato contra el HTML reparado, no contra lo que "debería" haber quedado.

    Aborta ese post concreto — sin tocar los demás — si detecta cualquiera de estas condiciones:

    • Aparece un <h1> inesperado en el cuerpo.
    • Hay un <p> metido dentro de un bloque <pre>.
    • Cambia el número de <pre>, <h2> o <img> respecto al original.
    • Se pierde algún enlace externo que existía antes de reparar.
    • El extractor, tras reparar, devuelve menos preguntas que antes de tocar nada.
    • El diagnóstico, tras reparar, sigue devolviendo fatal o bad en vez de pasar a ok.
    node scripts/fix-faq-schema.mjs --all          # dry-run: solo diagnostica
    node scripts/fix-faq-schema.mjs --all --apply  # repara de verdad
    

    El guardarraíl más importante es el último paso: después de escribir en WordPress, el script vuelve a leer el post ya guardado en el servidor y comprueba que el schema sale bien ahí —no en la respuesta que WordPress devolvió al hacer el POST—. No confía en que la escritura funcionó. Verifica que funcionó.

    El resultado, verificado — no asumido

    Categoría Antes de reparar Después de reparar
    Schema roto 112 posts 0 posts
    Schema válido 375 posts 487 posts
    Sin sección FAQ (no aplica) 106 posts 106 posts

    De los 487 posts con schema válido, 7 quedaron con un aviso cosmético menor — alguna pregunta sin signo de interrogación — que no bloquea el schema y no tiene impacto real. El resto: cero posts rotos.

    Cuándo esto no es suficiente

    Verificar por contrato no es magia, y sería deshonesto venderlo como si lo fuera.

    No arregla un contrato mal definido desde el principio. Si el contrato replicado por el script hubiera asumido, por ejemplo, que el extractor acepta <h4> cuando en realidad solo acepta <h3>, el script habría dado el visto bueno a posts que seguían rotos.

    Verificar contra un contrato equivocado da la misma falsa confianza que no verificar nada. El contrato hay que sacarlo del código real que consume el resultado, no de la memoria de quien escribió la plantilla hace dos años.

    La propia herramienta de verificación puede fallar, y necesita sus propios guardarraíles. Un script que repara HTML a golpe de expresiones regulares puede corromper contenido de formas que no están en su lista de comprobaciones.

    Por eso aborta ante solapes de edición, cambios en el número de imágenes o enlaces perdidos, o si el diagnóstico sigue en rojo después de reparar —pero esa lista la escribimos nosotros, pensando en lo que podía salir mal. Un caso que no anticipamos no queda cubierto. Esto no termina en un script que se audita a sí mismo una vez y ya: se sigue vigilando.

    Qué puedes hacer hoy

    Si mantienes contenido o código que un sistema automatizado consume después de ti —un schema, un feed, la salida de un agente que escribe en tu repo— deja de revisarlo leyendo el resultado final.

    Escribe (o pide a un agente que escriba) una función de verificación que replique exactamente lo que ese consumidor necesita, y corre esa función antes de aprobar nada. Es el mismo principio que explico con el AGENTS.md completo —contrato, carril y veredicto— en el ebook gratuito Revisión por Contrato.

    Si quieres ver cómo aplicamos esto a proyectos más grandes, con guardarraíles reales y no solo el argumento, en Dominicode Labs seguimos publicando los scripts y los casos según van pasando — este incluido.

    Preguntas frecuentes

    ¿Qué diferencia hay entre revisar código y verificarlo por contrato?

    Revisar código es leer el resultado —un diff, un HTML, una pantalla— y juzgar si parece correcto. Verificar código generado por IA por contrato es ejecutar, o replicar, el mismo proceso que usará el sistema que consume ese resultado, y comprobar que produce lo esperado.

    La revisión detecta si algo se ve bien; la verificación detecta si funciona para quien lo necesita, que casi nunca es un humano leyendo por encima.

    ¿Por qué el HTML se veía perfecto si el schema estaba roto?

    Porque "verse bien" y "cumplir el contrato" son criterios distintos. El navegador y el editor de WordPress renderizan cualquier combinación de etiquetas de forma legible, aunque esa combinación no sea la que un parser automatizado espera.

    El fallo solo existe desde el punto de vista del extractor, no desde el punto de vista de quien lee la página.

    ¿El schema FAQPage sigue sirviendo de algo si Google ya no muestra el rich snippet?

    Sigue siendo structured data válida, y sigue siendo el tipo de dato limpio y extraíble que un sistema automatizado —no un humano— necesita para leer tu contenido sin ambigüedad.

    Que Google retirara el desplegable visual del buscador en mayo de 2026 no cambia que ese contrato de fondo siga importando para cualquier motor que consuma tu página en vez de un lector.

    ¿Cómo verifico código generado por IA cuando lo escribe un agente en mi propio repo?

    Igual que aquí: define el contrato exacto que ese código debe cumplir —qué test tiene que pasar, qué estructura tiene que respetar— antes de que el agente escriba una sola línea.

    Después no apruebes el resultado leyendo el diff: corre ese contrato contra lo que el agente entregó. Si estás construyendo ese agente desde cero, el mismo principio aplica en cada uno de los 5 pasos. El método completo de verificación, con ejemplos de AGENTS.md, está en el post sobre Revisión por Contrato.

    ¿Qué pasa si el propio script de verificación tiene un error?

    Puede pasar, y por eso no basta con escribirlo una vez y confiar en él para siempre. Este en concreto se protege con guardarraíles explícitos —aborta si cambia el número de imágenes o enlaces, y relee el contenido ya guardado en el servidor en vez de asumir que la escritura funcionó.

    Pero esa lista de guardarraíles la definió una persona, y solo cubre lo que esa persona anticipó. Verificar por contrato reduce el margen de error; no lo elimina.


    Por Bezael Pérez — Developer senior con más de 15 años de experiencia y fundador de Dominicode.

  • Qué delegué a un agente de IA (y qué audité línea por línea)

    Qué delegué a un agente de IA (y qué audité línea por línea)

    Martes por la noche dejo un agente generando cuarenta y dos thumbnails para posts antiguos: prompt, imagen, nombre de archivo, carpeta correcta. Es de las tareas más fáciles de delegar a un agente de IA que tengo: patrón repetido, blast radius bajo, nadie la ve hasta que yo la reviso. Me voy a dormir sin abrir ni una carpeta.

    Miércoles tengo otro agente con el email del próximo lanzamiento montado en MailerLite: asunto, enlaces con UTM, la lista completa como destino. Solo falta el clic de "Enviar ahora". Con el cursor encima del botón, estoy a punto de aprobarlo con la misma mano suelta con la que aprobé los thumbnails el día anterior.

    No lo hago. Releo el asunto: el precio es el de la semana pasada, subió el martes y el agente trabajó con el contexto que tenía guardado. Si sale así, miles de bandejas reciben una cifra que ya no es verdad.

    Misma semana, misma herramienta —Claude Code—, dos posturas distintas frente al mismo tipo de trabajo. De eso va este post.

    En corto: delego a un agente de IA sin supervisión estrecha cuando el error es barato, reversible y fácil de detectar —thumbnails, primer borrador, research, formateo de archivos con patrón claro—. Audito línea por línea cualquier cosa que toque producción real: publicar, hacer commit o push, tocar dinero, o mandar un email a toda la lista. El criterio no es "confío en el agente": es cuánto cuesta que salga mal comparado con cuánto cuesta verificarlo antes de que salga.


    ¿Qué es el blast radius de una tarea delegada a un agente de IA?

    El blast radius de una tarea delegada a un agente es cuánto daño hace si sale mal, multiplicado por cuánta gente o cuánto dinero toca antes de que alguien lo note. No mide qué tan bueno es el modelo. Mide el sistema alrededor: qué tan fácil es deshacer lo que hizo y qué tan rápido te enteras si lo hizo mal.

    Renombrar cuarenta y dos imágenes tiene blast radius casi cero: si un nombre sale mal, lo veo al abrir la carpeta y lo arreglo en diez segundos. Mandar un email a toda la lista tiene blast radius alto: si el asunto está mal, ya salió, no hay deshacer.

    Trabajo solo en Dominicode, sin nadie que revise detrás de mí. Cada tarea que delego sin mirar es una tarea que, si falla, la descubro yo tarde, o la descubre el lector. Esa asimetría fija la frontera.

    Lo que audito siempre, sin excepción

    El email del miércoles no fue un accidente: es la categoría completa donde nunca delego la revisión, pase lo que pase el resto de la semana. Cualquier acción que toque producción real, dinero o algo irreversible.

    Ahí entra publicar en WordPress —el agente solo puede dejar el post en draft, nunca en publish—. Entra hacer commit y push a una rama compartida. Entra cualquier cifra de facturación o de precio. Y entra, desde esta semana, cualquier email a la lista completa sin que yo lea la última línea con los ojos abiertos.

    El agente no mintió: trabajó con el contexto que tenía. El precio cambió después del borrador y nadie le avisó. Eso no se arregla con "mejor prompt" — se arregla con un humano revisando antes de que salga, siempre. El método completo —contrato, carril y veredicto— lo dejo entero y gratis en el ebook Revisión por Contrato.

    Mi semana real: qué se delega y qué se audita

    Tarea Postura Blast radius Reversibilidad
    Generar thumbnails y portadas del blog Delego sin mirar Bajo — se ve al abrir la carpeta Total — se regenera con un comando
    Primer borrador de un post o guion Delego sin mirar Bajo — nadie lo lee hasta que yo lo apruebo Total — vive en un .md local
    Research y resumen de una discusión técnica Delego, verifico la fuente citada Bajo, y verificar el enlace cuesta un minuto Total
    Renombrar o formatear archivos con patrón repetido Delego sin mirar Bajo Alta — está versionado en git
    Commit y push Reviso el diff siempre Medio-alto — lo ve cualquiera que haga pull Media — revertir cuesta tiempo y ruido
    Publicar un post en WordPress Audito siempre; el agente solo deja draft Alto — lo ve el lector final Baja — un post mal publicado ya lo indexó Google
    Email de lanzamiento a toda la lista Audito línea por línea, incluidas cifras Muy alto — miles de bandejas de entrada Cero — no existe "deshacer enviar"
    Cualquier cosa que toque dinero o facturación Audito siempre, sin excepción Muy alto Depende del banco, no de mí
    Delegar sin gate automático (tests/tipos) que verifique el resultado No delego Alto, aunque no lo parece a simple vista Depende de si lo detectas a tiempo

    Esta tabla no es universal — cambia con tu stack. Si tu WordPress publica en directo sin pasar por borrador, esa fila sube dos puestos en tu lista de "auditar siempre". El criterio se traslada; los números, no.

    Es el flujo que enseño paso a paso en Construye con IA: cómo montar agentes que generan contenido, research y primeros borradores sin tener que mirar cada línea mientras trabajan. Si todavía no has montado uno, aquí explico cómo construir un agente de IA desde cero en 5 pasos.

    Lo que dice Hacker News cuando se discute esto mismo

    No soy el único con esta fricción. En mayo de 2026, un post de Simon Willison sobre dónde termina el vibe coding y empieza la ingeniería agéntica llegó a 787 puntos y más de 800 comentarios en Hacker News — casi todo el hilo discute esta frontera. Los comentarios citados abajo están traducidos del inglés; el enlace de cada uno lleva al original.

    Amber-chen lo resume en una frase que podría ser el resumen de este post:

    "La distinción entre 'vibe coding' e 'ingeniería agéntica' importa. La diferencia clave es si estás revisando y entendiendo el código que produce el agente. Cuando uso agentes para tareas no triviales, siempre reviso el diff antes de hacer commit — esa es la parte de ingeniería. El peligro es saltarse ese paso y confiar sin más en el resultado."

    arian_ apunta al problema real, que no es de habilidad sino de infraestructura:

    "La distancia entre 'vibe coding' e 'ingeniería agéntica' es la misma distancia entre pedirle a alguien que haga una tarea y poder demostrar que la hizo bien. Uno es intuición. El otro es rendición de cuentas. Seguimos construyendo agentes más potentes sin construir la infraestructura de auditoría para verificar qué hicieron de verdad."

    bhagyeshsp añade la pieza que falta: la distancia de responsabilidad entre quien produce el resultado y quien responde por él es lo que decide cuánto puedes soltar sin revisión. Cuanto más lejos estás de responder tú mismo por algo, menos deberías delegarlo sin mirar.

    No es cuestión de fe en el modelo. Es cuestión de quién responde si sale mal, y qué tan caro sale.

    El criterio, en tres preguntas — no en "cuánto confío"

    Cada vez que un agente termina una tarea, me hago tres preguntas, en este orden:

    1. ¿Cuál es el blast radius si esto sale mal? ¿Lo ve un archivo local o lo ve un lector, un cliente, un banco?
    2. ¿Es reversible? ¿Lo deshago en diez segundos o ya salió por la puerta?
    3. ¿Cuesta más verificarlo que hacerlo yo mismo? Si sí, delegar no ahorra nada — es teatro de productividad.

    La tercera es la que menos se hace la gente, y la más incómoda: hay tareas donde revisar línea por línea tarda casi lo mismo que hacerlas a mano. Ahí delegar no es progreso, es mover el trabajo de sitio y añadir riesgo encima. Por eso creo que el techo de los agentic systems no es la capacidad del modelo, sino el coste de verificar cada tarea.

    Cuándo NO delegar a un agente de IA

    Tres situaciones donde no delego sin mirar, aunque la tarea parezca sencilla:

    1. Cuando no hay un gate automático que verifique el resultado. Sin tests, sin tipos, sin criterios de aceptación que corran solos, no hay diferencia real entre dejar que un agente haga commit sin revisión y dejar que lo haga alguien el primer día en el puesto. Confianza sin verificación no es confianza, es esperanza.
    2. Cuando el error es barato de cometer pero caro o imposible de deshacer, aunque la probabilidad sea baja. Un email masivo, un post publicado, una cifra de facturación: la baja probabilidad no compensa un coste irreversible.
    3. Cuando el agente no tiene el contexto de negocio que cambió esta semana: un precio, una decisión editorial que solo existe en mi cabeza. Esto no se arregla con más contexto en el prompt — se arregla con un humano revisando antes de que la acción sea irreversible.

    Nada de esto es un argumento contra usar agentes. Es un argumento contra tratarlos todos igual.

    Qué hacer hoy con esto

    No necesitas una política de veinte páginas. Escribe, para las cinco tareas que más delegas esta semana, una columna de blast radius y una de reversibilidad. Las que salgan bajas en ambas, suéltalas del todo. Las que salgan altas en cualquiera de las dos, revísalas siempre, aunque el agente lleve un mes acertando.

    Si quieres ver cómo aplico esto cada semana en un negocio real que opero solo, sin equipo detrás que revise por mí, en Dominicode Labs comparto el criterio actualizado y los agentes concretos que uso para cada tarea.


    Preguntas frecuentes

    ¿Cómo decido qué tareas delegar a un agente de IA sin supervisión?

    Con tres preguntas: cuál es el blast radius si sale mal, si es reversible, y si verificarlo cuesta más que hacerlo tú mismo. Si el daño es bajo, se puede deshacer y verificar sale barato, delega sin mirar. Si cualquiera falla, revisa antes de que salga.

    ¿Qué es el blast radius aplicado a un agente de IA?

    Es cuánto daño hace una acción del agente si sale mal, multiplicado por cuánta gente o cuánto dinero toca antes de que alguien lo note. No mide la capacidad del modelo: mide el sistema alrededor, qué tan fácil es deshacer el error y qué tan rápido te enteras.

    ¿Puedo dejar que un agente de IA haga commit o push directamente a producción?

    No sin un gate automático que verifique el resultado antes —tests, tipos, criterios de aceptación—. Sin eso, un push sin revisión es como dejarlo hacer a alguien el primer día en el puesto: puede salir bien, pero no lo sabes hasta que ya pasó.

    ¿Qué diferencia hay entre vibe coding e ingeniería agéntica, según Hacker News?

    Según el hilo que generó el post de Simon Willison, la diferencia no está en la herramienta ni en el modelo: está en si revisas y entiendes lo que el agente produjo antes de aceptarlo. Vibe coding es confiar sin mirar. Ingeniería agéntica añade la disciplina de revisar el diff y responder por él.

    ¿Delegar a un agente de IA ahorra tiempo real si después tengo que revisarlo?

    Depende de cuánto tarde la revisión frente a hacer la tarea tú mismo. Si verificar te lleva casi lo mismo que escribirlo de cero, delegar no ahorra tiempo: mueve el trabajo y añade el riesgo de confiar de más porque "las últimas veces salió bien".


    Por Bezael Pérez — Developer senior con más de 15 años de experiencia y fundador de Dominicode.

  • Mejores herramientas de code review con IA 2026: precios y límites

    Mejores herramientas de code review con IA 2026: precios y límites

    En marzo de 2026 revisé una pull request de 340 líneas en un proyecto de un cliente. El bot de code review había dejado 23 comentarios. Los leí todos. Todos eran correctos.

    Mergeamos. Dos días después, producción se cayó por esa PR.

    El bot revisó el diff perfectamente. Lo que no hizo —lo que ninguna de las herramientas de code review con IA que he probado desde entonces hizo— fue preguntarse si ese endpoint debía existir.

    Nadie había escrito qué significaba "hecho" para esa tarea. El bot revisó el código contra la nada, y la nada siempre aprueba.

    En corto: las mejores herramientas de code review con IA en 2026 son CodeRabbit (24 $/dev/mes anual, la más pulida en PRs de GitHub), Greptile (30 $/asiento/mes, contexto de todo el repo y pago por créditos) y Claude Code con /review (incluida en cualquier plan de pago desde 17 $/mes con facturación anual, la que más control te da sobre el criterio: revisa contra tus reglas, no contra las suyas).

    GitHub Copilot code review (desde 10 $/usuario/mes) y Cursor BugBot son las opciones por defecto si ya pagas esas plataformas. Ninguna de las cinco decide contra qué se revisa tu código: eso lo defines tú antes, o no lo define nadie.

    ¿Qué es una herramienta de code review con IA?

    Una herramienta de code review con IA es un sistema que lee automáticamente el diff de una pull request, lo analiza con un modelo de lenguaje y publica comentarios sobre bugs, seguridad y calidad antes de que un humano lo mire.

    Esa definición es más importante de lo que parece por una palabra: diff. Todas estas herramientas parten del cambio, no del objetivo. Saben qué cambiaste. No saben qué querías conseguir.

    Comparativa de herramientas de code review con IA: 5 opciones y el baseline humano

    Precios consultados el 19 de septiembre de 2026 en las páginas oficiales de cada producto. Cambian a menudo — verifica antes de meter la tarjeta. La última fila no es una herramienta: es el coste del revisor humano, para que compares contra algo y no contra cero. Las licencias van en dólares porque así las publican los fabricantes; el coste humano va en euros por ser el mercado de referencia.

    Herramienta Precio Qué detecta bien Qué NO cubre Cuándo compensa
    CodeRabbit Essentials 24 $/dev/mes (anual) · 30 $/dev/mes (mensual). Team 48 $/dev/mes · Advanced 72 $/dev/mes. Gratis en repos open source públicos Bugs concretos en el diff, resúmenes de PR, linters y SAST integrados, 1-click fixes Si la feature era necesaria; decisiones de arquitectura que cruzan varios repos en el plan base (Essentials analiza 1 repo; Team, hasta 5) Equipos de 3-15 devs con muchas PRs pequeñas en GitHub
    Greptile Gratis (50 créditos/mes, 1 dev activo) · Pro 30 $/asiento/mes con 50 créditos, 1 $ por crédito extra Bugs reales con contexto de todo el repo; 1 review = 1 crédito, review TREX = 3 Su propia tasa de falsos positivos: no la publica ni ella ni ninguna competidora. Y no sustituye el juicio de producto Monorepos grandes donde el bug vive lejos del diff
    GitHub Copilot code review Pro 10 $/usuario/mes · Pro+ 39 $ · Max 100 $. El plan Free no lo incluye. Consume créditos de IA de GitHub Lo básico y estándar, dentro de github.com sin instalar nada No lee tus respuestas a sus propios comentarios y puede repetir comentarios que ya descartaste (doc oficial) Ya pagas Copilot y quieres una red de seguridad sin añadir otra factura — consume tus créditos de IA
    Cursor BugBot Incluido en Pro 20 $/mes, Pro+ 60 $, Ultra 200 $ (−20 % anual), con facturación por uso. Cursor no publica tarifa por revisión: consultar pricing oficial Bugs y problemas de seguridad en el diff, autofix vía Cloud Agent, reglas personalizadas Por defecto solo mira el código cambiado desde la revisión anterior; autofix limitado a 3 intentos por PR y sin "crear rama" en GitLab, Bitbucket y Azure DevOps Tu equipo ya vive dentro de Cursor y no quiere otra factura
    Claude Code (/review) Incluido en todo plan de pago: Pro 17 $/mes (anual) o 20 $/mes · Max desde 100 $/mes · Team 20 $/asiento (anual) Lo que tú le digas: corre como subagente contra tus reglas, tu AGENTS.md y tu spec Lo que no le digas que mire: sin contrato escrito revisa según su criterio, igual que las demás Ya tienes specs o reglas escritas y quieres revisar contra ellas, no contra el gusto del modelo
    Review manual humano 0 € de licencia. Ejemplo orientativo: 4 h/semana × 4 semanas = 16 h/mes; a 40 €/h son 640 €/mes por revisor Intención, producto, contexto de negocio, deuda técnica que importa Errores mecánicos cuando la PR es larga: la atención humana se degrada con la longitud del diff Siempre, encima de cualquier herramienta. No es una alternativa, es la capa que decide

    Análisis de cada herramienta de code review con IA: a favor y en contra

    Estas son las cinco herramientas de la tabla en detalle, con el argumento a favor y la objeción real de cada una. No es un ranking: el orden es el mismo de la tabla.

    CodeRabbit

    A favor: es la más pulida de las cinco en el flujo de GitHub. Los resúmenes de PR son útiles de verdad, la integración con linters y SAST evita duplicar herramientas, y el plan gratis para repos open source públicos no tiene truco.

    En contra: el precio escala rápido. El análisis multi-repo está en Team (48 $/dev/mes anual): Essentials analiza un repo, Team hasta cinco. Para un equipo de ocho devs son 4.608 $/año. Y sigue siendo una opinión sobre el diff.

    Greptile

    A favor: el contexto de repositorio completo es su gran ventaja. Encuentra el bug que está en el archivo que no tocaste. El modelo de créditos (50 incluidos por asiento, 1 $ el extra) es honesto para equipos que no revisan 500 PRs al mes.

    En contra: no hay dato público de falsos positivos, ni suyo ni de la competencia, así que el ruido lo vas a medir tú. Si tu equipo ya ignora al bot, ninguna herramienta arregla eso. El descuento del 50 % para startups pre-Series A ayuda, pero no arregla el ruido.

    GitHub Copilot code review

    A favor: cero fricción. Si ya pagas Copilot Pro (10 $/usuario/mes), lo activas y ya está. Vive dentro de github.com y no añade otro proveedor a tu superficie de seguridad.

    En contra: es la menos profunda del grupo, y la documentación oficial lo admite sin rodeos: no ve tus respuestas a sus comentarios y puede repetir los que ya descartaste. Además, ahora consume los créditos de IA de tu plan, así que no es tan "gratis" como parece.

    Cursor BugBot

    A favor: si tu equipo escribe en Cursor, BugBot cierra el círculo sin cambiar de contexto. Las reglas personalizadas por equipo, repo y proyecto son potentes.

    En contra: el precio es opaco. "Facturación por uso" sin tarifa pública es lo contrario de lo que necesitas para presupuestar. Y aquí aparece el problema de fondo que señaló Daksh Gupta, CEO de Greptile —parte interesada, conviene decirlo—: cuando el que escribe el código y el que lo revisa comparten modelo, harness y prompts, "fallan de formas parecidas". Cursor revisando código de Cursor es el juez y la parte.

    Claude Code (/review + revisión agéntica)

    A favor: CodeRabbit, Greptile Pro y BugBot te dejan añadir reglas encima de su criterio; aquí no hay criterio de fábrica que corregir. /review es una skill incluida —alias de /code-review— que corre en un subagente forkeado desde la v2.1.218, y puedes apuntarla a tus specs, tus reglas y tu definición de "hecho". Sin factura nueva si ya pagas Claude, aunque cada review consume los límites de uso de tu plan. Lo desarrollo en detalle en agentic code review con Claude Code.

    En contra: no es un producto de code review, es un motor. No hay dashboard, no hay métricas de equipo, no hay onboarding para el junior. Y si no le das contra qué revisar, te devuelve opiniones genéricas igual que los demás.

    Review manual humano

    A favor: es el único revisor que sabe por qué existe la feature.

    En contra: no escala con la velocidad a la que los agentes generan código. Ese es exactamente el cuello de botella de verificar código de IA: generar es gratis, verificar no.

    El hilo que conviene leer antes de pagar

    En enero de 2026, el propio CEO de Greptile publicó un artículo titulado "There is an AI code review bubble". Llegó a portada de Hacker News con 351 puntos y 249 comentarios a 19 de septiembre de 2026, y la discusión es más valiosa que cualquier tabla comparativa, incluida la mía.

    El comentario más votado, de trjordan, lo dice sin anestesia (traduzco del inglés, igual que el resto de citas de este hilo):

    "Si has llegado al punto de depender de un code review con IA para cazar bugs, has perdido el hilo. El propósito de una PR es compartir conocimiento y detectar huecos estructurales."

    Otro usuario, candiddevmike, sostiene en su opinión que ninguna de estas herramientas aporta un review significativo más allá de lo que encontraría un linter. Y cuando Gupta defendió Greptile citando que los autores de PRs habían respondido "great catch" 9.078 veces en siete días, tadfisher le contestó lo único que había que contestar: "una cifra así es un dato, no una evidencia". Sin el denominador —cuántos comentarios publicó Greptile esos siete días— 9.078 no se puede interpretar.

    Ese hilo no dice que las herramientas sean inútiles. Dice que estamos midiendo lo que no toca.

    Lo que ninguna de estas herramientas compra: el contrato

    Todas estas herramientas revisan el código. Ninguna decide contra qué se revisa.

    Un bot que comenta el diff sigue siendo una opinión sobre el diff. Una opinión rápida, barata y a menudo acertada — pero una opinión. Lo que falta antes es el contrato: qué tiene que cumplir ese código para considerarse terminado, qué casos límite son obligatorios, qué se rompe si cambia esta firma, qué comportamiento está garantizado a quien consume esto.

    Si ese contrato no está escrito, el bot inventa uno por ti. Y el contrato que inventa un modelo es el promedio de GitHub, no el de tu producto.

    Por eso comprar la herramienta no resuelve el problema. Resuelve la mitad mecánica y deja intacta la mitad que causa los incidentes. La secuencia correcta es la inversa: primero defines el contrato, luego eliges quién lo verifica — y entonces cualquiera de estas cinco herramientas se vuelve mucho más útil, porque le estás dando un criterio en vez de pedirle que adivine el tuyo. Ese es el método que explico en revisión por contrato para código de agentes, y el punto de partida está en el ebook gratuito Revisión por Contrato (30 páginas, sin coste).

    Cuándo NO necesitas ninguna de estas herramientas

    Como regla de pulgar, si haces menos de unas 20 PRs al mes. A ese volumen, 30 $/dev/mes por un revisor automático es peor inversión que dedicar dos horas a escribir la definición de "hecho" de tu equipo. El coste fijo de la herramienta no se amortiza y el ruido sí se acumula.

    Si tu equipo ya ignora los comentarios del bot. Esto pasa más de lo que se admite. Cuando la mayoría de los comentarios son nits de estilo, el equipo aprende a hacer scroll y el hallazgo bueno se pierde con el resto. Añadir una segunda herramienta empeora el problema. Mide cuántos hallazgos se resuelven antes del merge, no cuántos comentarios se publican.

    Si no tienes tests ni CI. Un bot de review encima de un pipeline inexistente es teatro. Primero el harness que rompe el build, después el revisor que opina. Integrar las revisiones de IA en el pipeline de CI/CD importa más que elegir marca.

    Si el problema real es que nadie sabe qué estáis construyendo. Ninguna herramienta de esta tabla arregla una spec inexistente. Ese es un problema de método, y lo trato entero en el libro de Spec-Driven Development.

    Qué hacer hoy

    Elige por contexto, no por ranking: si vives en GitHub, CodeRabbit; si tu bug vive lejos del diff, Greptile; si ya pagas Cursor o Copilot, usa lo que tienes; si quieres revisar contra tus propias reglas, Claude Code.

    Pero antes de pagar nada, haz esto: abre la última PR que rompió algo en producción y escribe en tres líneas qué contrato debería haber cumplido ese código. Si no puedes escribirlo, ninguna herramienta de esta tabla te habría salvado.

    Eso es justo lo que trabajamos en el workshop Contract Based Review Method: tres horas en nueve módulos para ir de un GitHub Issue a una pull request verificada, con el contrato escrito antes de que ningún bot opine. Sale el 2 de octubre de 2026, bajo demanda.

    Preguntas frecuentes

    ¿Cuál es la mejor herramienta de code review con IA en 2026?

    No hay una mejor en absoluto, hay una mejor por contexto. CodeRabbit es la opción más sólida para equipos en GitHub con muchas PRs pequeñas (24 $/dev/mes anual). Greptile gana en monorepos grandes donde el bug está fuera del diff (30 $/asiento/mes). Claude Code es la más flexible si ya tienes reglas o specs escritas, porque revisas contra tu criterio y no contra el del modelo.

    ¿Merece la pena pagar CodeRabbit o Greptile si ya tengo GitHub Copilot?

    Solo si tu problema es la profundidad del review. Copilot code review viene incluido desde el plan Pro (10 $/usuario/mes) y cubre lo básico, pero la documentación oficial reconoce que no lee tus respuestas a sus comentarios y que puede repetir los descartados. Si eso te frustra a diario, la herramienta especializada se paga sola. Si no, estás pagando dos veces por lo mismo.

    ¿Puede un code review con IA sustituir al revisor humano?

    No, y las cifras del sector no dicen lo contrario: las más citadas las publica quien vende la herramienta. La IA es buena cazando errores mecánicos en el diff; el humano es el único que sabe si la feature debía existir. El reparto sensato es: la IA revisa lo mecánico, el humano revisa la intención y la arquitectura.

    ¿Cuánto cuesta realmente añadir code review con IA a un equipo de 8 developers?

    Con CodeRabbit Essentials anual son 24 $ × 8 = 192 $/mes, unos 2.304 $/año. Con Greptile Pro, 30 $ × 8 = 240 $/mes, más los créditos extra si pasáis de 50 revisiones por asiento. Con Claude Code no hay factura nueva si el equipo ya tiene plan de pago, pero cada review consume los límites de uso del plan. Compáralo siempre con el coste del tiempo humano que esperas ahorrar, no con cero.

    ¿Qué es la revisión por contrato y en qué se diferencia de usar un bot?

    La revisión por contrato consiste en escribir, antes de generar el código, qué tiene que cumplir para darse por terminado: comportamiento garantizado, casos límite obligatorios y qué se rompe si cambia. El bot revisa el diff contra su criterio; la revisión por contrato revisa el diff contra el tuyo. Son complementarias, pero el orden importa: sin contrato, el bot inventa uno.

    ¿Existe alguna herramienta de code review con IA gratuita?

    Sí, con límites. CodeRabbit es gratis de forma permanente en repositorios open source públicos. Greptile tiene un plan Starter gratuito con 50 créditos al mes para un developer activo, y acceso libre para proyectos con licencia MIT o Apache. GitHub Copilot en su plan Free no incluye code review — ahí hay que pasar a Pro.


    Por Bezael Pérez — Developer senior con más de 15 años de experiencia y fundador de Dominicode.

  • Claude Code en monorepos: dale solo la rebanada que necesita

    Claude Code en monorepos: dale solo la rebanada que necesita

    Un cliente me pasó su monorepo el mes pasado. Nueve paquetes, pnpm workspaces, Turborepo por encima. Le pedí a Claude Code algo ridículamente pequeño: cambiar el tipo de una prop en packages/ui.

    Tres respuestas después me estaba proponiendo tocar el cliente HTTP del backend.

    No era un modelo tonto. Era yo. Había arrancado la sesión desde la raíz del repo, y trabajar con Claude Code en monorepos desde la raíz significa una cosa muy concreta: le has dado nueve paquetes de superficie para una tarea que vive en uno.

    Esto no es el problema del que ya escribí en Context Drift. Aquel es temporal: la sesión se alarga, el historial se pudre, el agente se olvida de la instrucción de la iteración 3. Este es espacial. Se degrada en el minuto uno, con la ventana medio vacía, porque el repo es grande y nadie le ha dicho qué parte del repo importa.

    La tesis del post es esta: en un monorepo, la decisión más importante que tomas no es qué prompt escribes. Es desde qué directorio arrancas el agente.

    Smart context slicing es la práctica de arrancar el agente en el subárbol mínimo del monorepo que la tarea necesita, calculado a partir del grafo de dependencias en vez de a ojo. Son tres decisiones concretas: desde qué directorio lanzas claude, qué paquetes vecinos añades con --add-dir y qué rutas bloqueas con reglas de denegación. Las tres, en ese orden, son el resto del post.

    Claude Code en monorepos: un CLAUDE.md en la raíz no escala

    La documentación de Anthropic recomienda mantener cada CLAUDE.md por debajo de 200 líneas, y lo justifica: los archivos largos consumen más contexto y reducen la adherencia a las instrucciones.

    Ahora divide. Nueve paquetes, 200 líneas: 22 líneas por paquete para explicar su stack, sus convenciones y sus trampas.

    Así que solo hay dos finales, y he visto los dos.

    O el CLAUDE.md crece hasta las 600 líneas y el agente ignora la mitad — incluidas las reglas que importaban. O se queda genérico ("usa TypeScript estricto", "escribe tests"), que es una forma elegante de no decir nada.

    Si todavía estás montando el tuyo, el punto de partida lo dejé en CLAUDE.md: el system prompt de tu proyecto. Aquí doy por hecho que ya lo tienes y se te ha quedado pequeño.

    La solución es partirlo: raíz para lo global, un archivo por paquete para lo local.

    monorepo/
      CLAUDE.md                 # reglas globales: commits, estilo, "corre los scripts desde el paquete"
      packages/
        ui/CLAUDE.md            # convenciones de componentes, tokens de diseño
        api/CLAUDE.md           # Knex, migraciones, .env obligatorio
        web/CLAUDE.md           # rutas, data fetching
    

    Pero partirlo no sirve de nada si no entiendes cuándo se carga cada trozo.

    La regla de carga que casi nadie ha leído

    Claude Code no trata igual a los CLAUDE.md que están por encima de ti y a los que están por debajo.

    Dónde vive el CLAUDE.md Cuándo entra en contexto
    Tu directorio de trabajo y todos sus ancestros Al arrancar la sesión, siempre
    Subdirectorios por debajo de ti Bajo demanda, solo cuando el agente lee un archivo de esa carpeta

    Si arrancas desde la raíz, cargas solo el CLAUDE.md raíz — y vas acumulando el de cada paquete que el agente toque. Toca muchos, porque no sabe dónde está el límite.

    Si arrancas con cd packages/ui && claude, cargas raíz + packages/ui de golpe, y los de api y web no existen para esa sesión mientras no los pises. Además, solo puede leer y editar dentro de ese subárbol hasta que le concedas más.

    Eso es una rebanada. Y te ha costado un cd.

    Compruébalo: lanza /context y mira la lista de Memory files. Ahí está lo que se cargó de verdad.

    El slice no lo decides tú: lo decide el grafo de dependencias

    "Trabaja desde el paquete" está bien hasta que la tarea toca de verdad a los vecinos. Cambiar un tipo exportado de ui puede romper a quien lo consume, y si el agente no ve a esos consumidores, te entrega algo que compila en su rebanada y revienta en CI.

    La pregunta correcta no es qué paquetes te apetece abrir, sino qué paquetes toca esta tarea de verdad. Y esa respuesta ya está en tu repo: en el grafo de dependencias.

    Monté un workspace de cinco paquetes para verlo, con pnpm 11.1.3 y Turborepo 2.10.12. @acme/api y @acme/web dependen de @acme/ui; @acme/ui depende de @acme/config; @acme/jobs va por libre.

    Inventario primero:

    pnpm ls -r --depth -1
    

    Ahora el blast radius hacia arriba — qué se rompe si toco @acme/ui. En la sintaxis de filtros de pnpm, los tres puntos delante del nombre significan "y todo lo que depende de él":

    pnpm --filter "...@acme/ui" ls --depth -1
    # (salida recortada al nombre de cada paquete)
    # @acme/ui
    # @acme/api
    # @acme/web
    

    Y hacia abajo, con los puntos detrás, "y todo aquello de lo que depende":

    pnpm --filter "@acme/ui..." ls --depth -1
    # (salida recortada)
    # @acme/ui
    # @acme/config
    

    Si quieres el cierre completo en los dos sentidos, pones los puntos a ambos lados: "...@acme/ui...". Y si te sobra el propio paquete, el circunflejo lo excluye: "...^@acme/ui" devuelve solo api y web.

    Turborepo lo da con un matiz. --dry enseña el plan sin ejecutar nada:

    turbo run build --filter="...@acme/ui" --dry
    
    • Packages in scope: @acme/api, @acme/ui, @acme/web
    • Running build in 3 packages
    

    El detalle que solo ves ejecutándolo: "Packages in scope" son 3, pero si sacas el JSON aparecen 4 tareas:

    turbo run build --filter="...@acme/ui" --dry=json | jq -r '.tasks[].directory' | sort -u
    # packages/api
    # packages/config
    # packages/ui
    # packages/web
    

    @acme/config no está en el scope de edición, pero entra en el grafo de build porque ui lo necesita compilado. Son dos rebanadas distintas y conviene no confundirlas:

    Rebanada Paquetes % del repo
    Repo completo 5 100%
    Slice de edición (ui + dependientes) 3 60%
    Slice de build (añade config) 4 80%
    Nunca entra (@acme/jobs) 1 20%

    En un repo de cinco paquetes, dejar fuera un paquete suena a poco. En el del cliente, con nueve, el slice real de la tarea eran tres paquetes: dos tercios del repo que no tenían por qué abrirse nunca.

    Con esa lista en la mano, el arranque deja de ser una corazonada:

    cd packages/ui
    claude --add-dir ../api --add-dir ../web
    

    Si el equipo entero trabaja así, lo fijas en packages/ui/.claude/settings.json:

    {
      "permissions": {
        "additionalDirectories": ["../api", "../web"]
      }
    }
    

    Ojo con una diferencia que muerde: additionalDirectories da acceso a los ficheros pero no carga nunca el CLAUDE.md ni las skills de esos directorios. Con --add-dir sí cargan las skills, y el CLAUDE.md solo si arrancas con CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1. Si escribiste un CLAUDE.md en packages/api y no sale en /context, es por esto.

    Y como el slice te dice qué se puede romper, también sabes qué verificar antes de dar la tarea por buena: los tests de api y web, no los de ui. Convertir "parece que funciona" en un veredicto ejecutable lo desarrollé entero en el ebook gratuito Revisión por Contrato, sobre cómo revisar lo que te entrega un agente sin leértelo línea a línea.

    Lo que no debe entrar en la ventana bajo ningún concepto

    Las búsquedas de contenido de Claude Code respetan tu .gitignore por defecto, así que node_modules/, dist/ y build/ ya están fuera de los resultados de un grep.

    El problema es lo que sí está commiteado: código generado, un SDK vendorizado, snapshots enormes. Para eso hay reglas de denegación:

    {
      "permissions": {
        "deny": [
          "Read(./**/dist/**)",
          "Read(./**/*.generated.*)",
          "Read(./vendor/**)"
        ]
      }
    }
    

    Un detalle que rompe esto sin avisar: los patrones relativos anclan en el directorio desde el que arrancas la sesión, no en la raíz del repo. Si guardas estas reglas en la raíz pero lanzas la sesión desde packages/ui, Read(./vendor/**) está apuntando a packages/ui/vendor/. Para que apliquen en todo el repo las escribes absolutas, con doble barra: Read(//ruta/absoluta/al/repo/vendor/**).

    Y si arrancando desde la raíz se te cuelan los CLAUDE.md de equipos con los que no trabajas, existe claudeMdExcludes, en el .claude/settings.local.json de la raíz. Los patrones se comparan contra rutas absolutas, así que empiezan por **/ para que casen en cualquier punto del árbol:

    {
      "claudeMdExcludes": ["**/packages/legacy-*/**"]
    }
    

    Con un aviso honesto: esa lista es estática, no un interruptor por tarea. Para alternar de paquete cada día la herramienta sigue siendo el cd.

    Cuando de verdad no sabes dónde está, delega la búsqueda

    Todo lo anterior asume que sabes qué paquete tocar. A veces no lo sabes, y ahí es donde la gente destroza la sesión: "busca en el repo dónde se genera el token de refresco". El agente lee doscientos archivos y te devuelve una frase. Los doscientos archivos se quedan en tu ventana. La frase también, pero ya da igual.

    Delégalo a un subagente. Corre en su propia ventana de contexto y te devuelve el resumen, no los archivos:

    Usa un subagente para localizar en qué paquetes se genera y se valida
    el token de refresco. Devuélveme solo la lista de rutas y una línea
    por cada una. No propongas cambios todavía.
    

    El resultado es una lista de paquetes. Cierras la sesión, haces cd al correcto y empiezas la tarea real con la ventana limpia. La exploración se paga una vez y se tira.

    Es el mismo principio que conté en Context Engineering: lo caro no es el token, es el token irrelevante que se queda mirándote el resto de la sesión.

    Lo que puedes hacer hoy en tu monorepo

    Una sola cosa, y es gratis: deja de arrancar el agente desde la raíz del monorepo.

    Antes de la próxima tarea, corre pnpm --filter "...<tu-paquete>" ls --depth -1, mira los tres o cuatro nombres que salen, y arranca así:

    cd packages/<tu-paquete>
    claude --add-dir ../<vecino>
    

    No hace falta que escribas ni un CLAUDE.md nuevo para notar la diferencia. Eso viene después, cuando ya sepas qué reglas son globales y cuáles de un paquete — y eso solo se ve claro tras unos días trabajando por rebanadas.

    Si quieres el flujo completo, de la idea al producto con estas decisiones tomadas antes de escribir código, es lo que montamos en el curso Construye con IA.

    Preguntas frecuentes

    ¿Es mejor arrancar Claude Code desde la raíz del monorepo o desde el paquete?

    Desde el paquete, salvo que la tarea cruce varios subsistemas de verdad. Arrancando desde packages/ui cargas el CLAUDE.md raíz más el de ui, y el agente solo puede leer y editar ese subárbol. Desde la raíz tienes acceso a todo: útil para refactors transversales, caro para cualquier otra cosa. Si necesitas un vecino puntual, --add-dir te lo añade sin romper el aislamiento.

    ¿Los CLAUDE.md de los subdirectorios se cargan siempre?

    No, y esta es la confusión más habitual. Los de tu directorio de trabajo y de todos sus ancestros se cargan al arrancar la sesión. Los de subdirectorios por debajo de ti se cargan bajo demanda, solo cuando el agente lee un archivo de esa carpeta. Para ver qué se cargó de verdad en una sesión, lanza /context.

    ¿Qué hago si la tarea toca varios paquetes a la vez?

    Dásela entera en una sola sesión, con el slice completo delante. Partirla en una sesión por paquete es peor: cada sesión redecide el diseño desde cero y acabas con tres criterios distintos. Calcula el slice con el filtro de dependientes, añade esos directorios y trabaja en plan mode antes de editar: el plan se escribe a un archivo que Claude Code reinyecta tras cada compactación.

    ¿Esto sirve si uso Nx o si mi repo es un solo árbol grande sin paquetes?

    Sí. En Nx el equivalente es nx graph para ver el grafo y nx show projects --affected para saber qué proyectos toca un cambio: cambia el comando, no la idea. Y en un repo de un solo árbol sustituyes "paquete" por "subsistema" — src/billing/, src/auth/, lib/core/. Un CLAUDE.md por subsistema y un cd hacen el mismo trabajo.

    ¿No basta con el .gitignore para que el agente no lea dist?

    Para las búsquedas de contenido sí: Claude Code respeta el .gitignore por defecto, así que dist/, build/ y node_modules/ no aparecen cuando busca texto. Lo que no cubre es lo commiteado — código generado, SDKs vendorizados, fixtures gigantes. Para eso necesitas reglas Read(...) en permissions.deny. Con un límite: cubren las herramientas de fichero y los comandos de Bash que Claude Code reconoce, pero un grep -r sobre una carpeta con ficheros denegados sigue sacándolos por pantalla.


    Por Bezael Pérez — Developer senior con más de 15 años de experiencia y fundador de Dominicode.

  • Refactorizar código legacy con IA: el método SDD en brownfield

    Refactorizar código legacy con IA: el método SDD en brownfield

    El fichero se llamaba pricing.ts, tenía 1.100 líneas y un comentario en la línea 3 que decía // NO TOCAR — hablar con Javi antes. Javi se había ido de la empresa en 2021.

    Cero tests. Cero documentación. Y toda la facturación pasando por ahí.

    Hice lo que hace todo el mundo la primera vez que intenta refactorizar código legacy con IA: se lo pegué entero a Claude Code y le pedí que lo dejara limpio. Me devolvió algo precioso. Funciones puras, nombres decentes, 300 líneas en vez de 1.100.

    Y roto para los pedidos que acumulaban cupón y descuento de socio a la vez.

    El agente no alucinó nada. Hizo exactamente lo que le pedí. Le pedí arreglar un código cuya intención nadie le había explicado, porque nadie la sabía.

    Esa es la tesis de este post: en legacy la spec no describe la feature que quieres, describe el comportamiento que ya tienes. Y por eso el primer artefacto no es spec.md, son los tests de caracterización.

    Por qué refactorizar código legacy con IA falla sin tests

    Refactorizar código legacy con IA usando SDD consiste en invertir el ciclo habitual: primero tests de caracterización que congelan el comportamiento observable, después una spec que documenta lo que el sistema ya hace, y solo entonces plan y tasks.

    El motivo es simple. Un modelo lee código y ve perfectamente qué hace. Lo que no puede ver es qué debería hacer.

    En un proyecto nuevo eso da igual, porque la intención está en tu cabeza y la escribes tú. Es lo que hacemos cuando arrancamos un greenfield con slices verticales: la spec va delante porque describe algo que todavía no existe.

    En legacy la intención está enterrada bajo seis años de parches de viernes por la tarde. Y ahí aparece el problema real: ningún modelo distingue una regla de negocio rara de un bug que lleva años tolerándose.

    En mi pricing.ts había un Math.floor donde cualquiera pondría Math.round. Claude lo "arregló". Llevaba ahí desde 2019 porque el departamento financiero quería redondear siempre a favor del cliente.

    Eso no es un bug. Es un requisito no escrito. Y el agente no tenía forma humana de saberlo.

    Los antipatrones de este escenario los desarrollé en los 5 errores fatales al refactorizar legacy con IA, así que no los repito. El método positivo empieza invirtiendo el orden.

    Spec greenfield Spec brownfield
    Qué describe Lo que quieres construir Lo que ya hace el sistema
    Fuente de verdad Tu criterio de producto El código en producción
    Primer artefacto spec.md Tests de caracterización
    Criterio de éxito Cumple los casos de uso nuevos No cambia ninguna salida observable
    Ambigüedad Se resuelve preguntando Se resuelve ejecutando
    Riesgo principal Construir lo que no toca Romper lo que ya funcionaba

    En greenfield el ciclo es spec → plan → tasks → código. En brownfield es tests → spec → plan → tasks → código. La spec sigue existiendo, pero llega en segundo lugar: hasta que no ejecutas el módulo no sabes qué escribir en ella.

    Paso 0 — Acota el blast radius antes de abrir el editor

    La regla que más refactors me ha salvado: si no puedes escribir en una línea qué NO vas a tocar, no empieces.

    Escribe estas cuatro cosas antes de nada:

    • Dentro: src/pricing.ts y sus dos helpers.
    • Fuera: el modelo de datos, los endpoints, la UI de checkout.
    • Consumidores: quién importa esto. Lanza un rg sobre el repo y pega la lista tal cual.
    • Contrato público: las funciones exportadas que otros usan. Esas firmas no se tocan.

    Ese último punto es el que hace el trabajo acotable: si la frontera del módulo se mueve, ya no es un refactor, es un rediseño.

    Paso 1 — Arqueología asistida: el agente lee, no escribe

    Aquí Claude Code es brutalmente bueno, y es la parte que casi nadie usa. El agente tiene prohibido cambiar una sola línea.

    El prompt que uso, más o menos literal:

    Lee src/pricing.ts. No propongas mejoras ni refactorices nada.
    
    Produce docs/legacy/pricing-observado.md con:
    1. Cada rama de decisión del módulo, con la condición exacta que la activa.
    2. Las entradas: tipos reales, no los declarados. Marca los que en la práctica
       llegan como null o undefined.
    3. Las salidas: forma del retorno en cada rama.
    4. Efectos secundarios: I/O, escrituras, logs, mutación de argumentos, lecturas
       de Date/Math.random o de variables globales.
    5. Una sección "Comportamientos sospechosos": cosas que parecen bugs.
       NO las arregles. Solo lístalas con número de línea.
    6. Una sección "Preguntas que no puedo responder leyendo el código".
    

    Las secciones 5 y 6 son el oro: una lista lo que el agente habría "arreglado" solo, la otra lo que tienes que ir a preguntarle a un humano o a los logs de producción.

    En pricing.ts la sección 6 tenía nueve preguntas. Siete las resolví mirando datos reales. Dos las resolvió el responsable de facturación en cinco minutos. Ese día no escribí código y fue el día más productivo del refactor.

    Paso 2 — Tests de caracterización: congela el comportamiento, incluso el feo

    Un test de caracterización no comprueba que el código sea correcto. Comprueba que sigue haciendo lo mismo. En TDD el test va delante y define lo deseable; aquí va detrás y define lo existente.

    Aunque lo existente sea horrible.

    // pricing.characterization.test.ts
    import { describe, it, expect } from 'vitest'
    import { calcularPrecioFinal } from '../src/pricing'
    
    // Casos capturados de pedidos reales de producción, anonimizados.
    const CASOS = [
      { nombre: 'base sin descuentos', pedido: { subtotal: 100, cupon: null, pais: 'ES', socio: false } },
      { nombre: 'cupon y socio acumulados', pedido: { subtotal: 100, cupon: 'VIP10', pais: 'ES', socio: true } },
      { nombre: 'cupon caducado', pedido: { subtotal: 100, cupon: 'OLD20', pais: 'ES', socio: false } },
      { nombre: 'pais sin IVA', pedido: { subtotal: 100, cupon: null, pais: 'US', socio: false } },
      { nombre: 'decimales feos', pedido: { subtotal: 1234.56, cupon: 'VIP10', pais: 'ES', socio: true } },
      { nombre: 'subtotal cero', pedido: { subtotal: 0, cupon: 'VIP10', pais: 'ES', socio: true } },
    ] as const
    
    // CONGELADO: el caso 'decimales feos' devuelve un céntimo de menos por el
    // Math.floor de pricing.ts:412. Se arregla DESPUÉS del refactor, en un
    // commit propio. Ver LEG-14.
    describe('calcularPrecioFinal — caracterización', () => {
      it.each(CASOS)('$nombre', ({ pedido }) => {
        expect(calcularPrecioFinal(pedido)).toMatchSnapshot()
      })
    })
    

    Fíjate en lo que no hay: ningún valor esperado escrito a mano. El snapshot lo genera la primera ejecución. Tú no decides la salida correcta, la registras.

    El término viene de Working Effectively with Legacy Code (Michael Feathers, 2004), y en Vitest 5 lo implementas con toMatchSnapshot().

    Después abres el fichero de snapshots y lo lees entero. Ahí aparecen las sorpresas y ahí apuntas los // CONGELADO:. Cada uno es un ticket futuro, no una excusa para tocar nada ahora.

    Y sí, congelas el bug a propósito. Si arreglas comportamiento y estructura en el mismo commit, cuando algo falle en producción no sabrás cuál de las dos cosas lo rompió.

    Paso 3 — La spec brownfield

    Ahora, y solo ahora, escribes la spec. Con los tests en verde delante deja de ser un ejercicio de memoria, y las secciones que importan no son las de un proyecto nuevo:

    # Spec — Refactor de pricing
    
    ## Comportamiento observado
    Documentado en docs/legacy/pricing-observado.md.
    Congelado en pricing.characterization.test.ts (6 casos).
    
    ## Contrato público (NO cambia)
    calcularPrecioFinal(pedido: Pedido): Precio
    - Devuelve `total` en céntimos como number. No se migra a bigint en este refactor.
    - Nunca lanza: ante entrada inválida devuelve { total: 0, error: string }.
    
    ## Efectos secundarios actuales
    - Escribe en la tabla pricing_audit. SE MANTIENE.
    - Lee process.env.TAX_MODE en caliente. SE MANTIENE, se aísla en config.ts.
    - Muta el objeto `pedido` recibido. SE ELIMINA: ningún consumidor depende de
      ello, verificado en los 4 call sites.
    
    ## Deuda congelada a propósito
    - LEG-14: redondeo con Math.floor en la línea 412.
    - LEG-15: cupón caducado devuelve descuento 0 en vez de error.
    
    ## Fuera de alcance
    Modelo de datos, endpoints, UI de checkout, migración a bigint.
    
    ## Criterio de aceptación
    Los 6 tests de caracterización pasan sin modificar sus snapshots.
    El test de equivalencia legacy/refactor pasa en las 72 combinaciones.
    

    Es corta a propósito. Y es lo que le das al agente en cada task, no el fichero de 1.100 líneas.

    El formato completo lo tienes en el libro de Spec-Driven Development. Para el esqueleto uso el skill dominicode-sdd-creator, que genera spec.md + plan.md + tasks.md; el contenido brownfield lo pones tú, porque sale de los tests.

    Si dudas de cuánta ceremonia merece el módulo, el criterio está en los tres niveles de SDD. Un refactor de legacy con dinero de por medio es nivel alto, sin discusión.

    Paso 4 — Plan por fases, tasks pequeñas, un commit verde cada una

    El plan de un refactor brownfield tiene siempre la misma forma:

    1. Aislar. Extraer funciones puras sin cambiar la lógica. Copiar, no reescribir.
    2. Tipar los bordes. Con los tipos reales del paso 1, no los declarados.
    3. Sustituir por partes. La implementación nueva convive con la vieja mientras dure.
    4. Borrar el legacy. Cuando la equivalencia lleve dos semanas en verde.

    La fase 3 es la que necesita andamio. Copia el original a pricing.legacy.ts, deja pricing.ts para la implementación nueva, y este es todo el andamio:

    // pricing.equivalence.test.ts
    import { describe, it, expect } from 'vitest'
    import { calcularPrecioFinal as legacy } from '../src/pricing.legacy'
    import { calcularPrecioFinal as refactor } from '../src/pricing'
    
    const subtotales = [0, 9.99, 100, 1234.56]
    const cupones = [null, 'VIP10', 'OLD20']
    const paises = ['ES', 'US', 'DE']
    const socios = [true, false]
    
    describe('legacy vs refactor — equivalencia', () => {
      for (const subtotal of subtotales) {
        for (const cupon of cupones) {
          for (const pais of paises) {
            for (const socio of socios) {
              const pedido = { subtotal, cupon, pais, socio }
              it(`${subtotal} / ${cupon ?? 'sin cupon'} / ${pais} / socio=${socio}`, () => {
                expect(refactor(pedido)).toEqual(legacy(pedido))
              })
            }
          }
        }
      }
    })
    

    72 combinaciones que el agente ejecuta solo cada vez que cierra una task. Y ojo: si el test sale intermitente no tienes un problema de refactor, tienes un Date.now() o un Math.random() sin inyectar. Arréglalo antes de seguir.

    Regla de tamaño de task: si el diff no lo puedes leer entero en diez minutos, pártela. El límite no lo pone el agente, lo pone tu capacidad de revisar lo que produjo — que es el verdadero cuello de botella de trabajar con agentes.

    Paso 5 — Qué haces cuando un test se pone rojo

    Un test de caracterización en rojo tiene tres causas. Míralas en este orden.

    Uno: el refactor rompió algo. Nueve de cada diez veces, por mi experiencia. Revierte la task, no la parchees: el diff es pequeño precisamente para que revertir sea barato.

    Dos: el refactor arregló un bug sin querer. Pasa más de lo que parece y es una trampa. Revierte igual y arréglalo en su propio commit, con su snapshot actualizado. Un cambio de comportamiento colado dentro de un refactor pasa desapercibido en la review casi siempre.

    Tres: el test no era determinista. Fechas, aleatoriedad, orden de un Object.keys, zona horaria. Eso no es caracterización, es ruido. Arréglalo en el test o inyecta la dependencia.

    La regla que resume el paso 5 entero: un refactor nunca cambia comportamiento, y un cambio de comportamiento nunca se llama refactor. Commits distintos, PRs distintas, riesgos distintos.

    Este bucle es el mismo que aplico en TDD potenciado por IA, solo que en legacy los tests no los escribes para diseñar: los escribes para tener permiso a tocar.

    Cómo empezar a refactorizar legacy con Claude Code el lunes

    Coge el fichero que todo el mundo evita en tu repo. No lo refactorices. Haz solo esto, y no tardas más de una hora.

    Escribe en una línea qué entra y qué queda fuera. Lanza a Claude Code el prompt de arqueología del paso 1 en modo lectura. Y escribe cinco tests de caracterización con los casos que ya te sabes de memoria, porque son los que se rompen cada trimestre.

    El lunes no refactorizas nada. El martes ya puedes, y con red.

    Cuando quieras montar la verificación en serio — el AGENTS.md, los carriles del agente y los criterios que se comprueban solos — está en el ebook gratuito de Revisión por Contrato. Y el ciclo completo de idea a producto con Claude Code ejecutando tasks es el recorrido del curso Construye con IA.

    El código legacy no da miedo por antiguo. Da miedo porque no sabes qué hace. Y eso se arregla escribiendo tests, no reescribiendo código.

    Preguntas frecuentes

    ¿Qué es un test de caracterización y en qué se diferencia de un test unitario normal?

    Un test unitario afirma que el código hace lo correcto. Un test de caracterización afirma que sigue haciendo lo mismo que antes, sea correcto o no. No lo escribes a mano: ejecutas el módulo con entradas reales y registras la salida en un snapshot. Su único trabajo es ponerse rojo cuando el refactor cambia una salida observable.

    ¿Merece la pena congelar un comportamiento que sé que es un bug?

    Sí, siempre. Si arreglas el bug en el mismo commit en el que reestructuras el código y algo revienta en producción, no podrás distinguir cuál de las dos cosas lo rompió. Congélalo con un comentario que explique la sospecha y su ticket, y arréglalo después en un commit propio donde el cambio de snapshot sea la parte visible de la pull request.

    ¿Cuánto código legacy le puedo dar a Claude Code de una vez?

    Menos del que cabe. El límite útil no es la ventana de contexto, es lo que tú puedes verificar después. Yo trabajo módulo a módulo y en cada task le paso la spec brownfield y los tests, no el fichero original. Una vez documentado el comportamiento en el paso 1, ese documento sustituye al código fuente como contexto.

    ¿Puedo saltarme los tests de caracterización si el módulo ya está tipado con TypeScript estricto?

    No. Los tipos garantizan la forma del dato, no el valor. Un refactor que cambia Math.floor por Math.round, que invierte el orden de dos descuentos o que redondea antes en vez de después compila perfecto, pasa el type-check y factura mal. Los tipos protegen el contrato; los tests de caracterización protegen el comportamiento.

    ¿Y si el módulo legacy no se puede ejecutar de forma aislada?

    Entonces esa es tu primera task, y no es refactorizar. Si no puedes invocar la función sin levantar media aplicación, lo que falta es una costura: inyectar la base de datos, el reloj y las llamadas HTTP para poder ejecutarla con entradas controladas. Feathers lo llama seam. Hasta que no consigues ejecutar el módulo con entradas que tú decides, no hay tests de caracterización posibles ni refactor seguro.

    ¿Sirve este método si el módulo legacy no está en TypeScript?

    Sí, el orden no cambia. Lo único que necesitas es un runner con snapshots: pytest con syrupy en Python, ApprovalTests en Java o C#, o el propio Vitest si es JavaScript sin tipar. Lo que sí cambia es el paso de tipar los bordes: sin tipos estáticos pierdes la red del compilador y el peso recae entero sobre los tests de caracterización, así que conviene capturar más casos de los que capturarías en TypeScript.


    Por Bezael Pérez — Developer senior con más de 15 años de experiencia y fundador de Dominicode.

  • Programar con IA: el cuello de botella es verificar, no escribir

    Programar con IA: el cuello de botella es verificar, no escribir

    Hace un año le pedía una feature al agente y me devolvía doscientas líneas.

    Hoy me devuelve ochocientas, y tarda menos.

    Mi ritmo de entrega es exactamente el mismo.

    El cuello de botella dejó de ser escribir código. Ahora es verificarlo — y eso lo sigo haciendo yo, a mano.

    Durante meses lo achaqué a cosas mías: mala semana, tarea rara, repo complicado. Hasta que hice el cálculo aburrido de cuánto tiempo pasaba generando y cuánto verificando código generado por IA. Generar: cuatro minutos. Verificar: casi dos horas.

    Cuadruplicar la velocidad de los cuatro minutos no me iba a devolver ni un minuto de las dos horas.

    Goldratt lo dejó escrito en La Meta, en 1984, y sigue siendo la frase más útil que conozco para esto: una hora ganada donde no está el cuello de botella es un espejismo. Toda la industria lleva tres años ganando horas justo ahí.

    El cuello de botella se movió y nadie avisó

    El cuello de botella del desarrollo con IA se movió de escribir código a verificarlo. Los datos van en la misma dirección que la anécdota.

    El informe DORA de 2024 midió algo que a mucha gente le sentó fatal: por cada 25% de aumento en la adopción de IA en una organización, el throughput de entrega bajaba un 1,5% estimado y la estabilidad un 7,2%. Más código, menos entrega, y bastante menos tranquilidad.

    Lo interesante es lo que pasó después. En la edición de 2025 el throughput ya sale positivo: aprendimos a mover el código generado hasta producción sin atascarnos tanto. Pero la relación con la estabilidad sigue siendo negativa.

    Traducido: arreglamos la velocidad. No arreglamos la confianza.

    Y no es raro, porque entre el código que sale del agente y producción hay un paso que no ha mejorado nada en tres años: alguien tiene que decidir si eso es correcto. Ese alguien eres tú, con el mismo cerebro de 2019 y menos horas de sueño.

    Es el único componente del sistema que no escala, y es el que recibe todo lo que los demás producen más rápido.

    Por eso un modelo mejor no lo arregla. Un modelo mejor te da código correcto más a menudo — te sube el porcentaje de aciertos, no te quita la obligación de comprobar. Y como no sabes de antemano cuál de las ochocientas líneas es la que falla, sigues teniendo que mirarlas todas.

    Un modelo con un 97% de acierto sobre ochocientas líneas te deja veinticuatro líneas malas escondidas y ninguna pista de dónde.

    Qué es la Revisión por Contrato

    Revisión por Contrato es un método para verificar código generado por IA sin leerlo entero, moviendo la verificación de tu cabeza a procesos que se ejecutan solos. Son tres piezas, en este orden:

    Contrato. Lo que se construye, escrito de forma que una máquina pueda rechazarlo. No "el endpoint debe ser rápido", sino "p95 por debajo de 200 ms en el test de carga del CI". Vive en dos sitios: el AGENTS.md para lo permanente del repositorio, la spec para lo que nace y muere con esta tarea.

    Carril. Por dónde el agente no puede salirse. Qué ficheros no toca, qué no instala, qué asserts existentes no modifica. Porque la mayoría de los desastres de un agente no son de lógica: son de alcance.

    Veredicto. Quién dice que está bien. Un conjunto de comandos con dos estados posibles y ninguno más: build, tipos, lint, tests y — la que casi nadie tiene — un comando detrás de cada criterio de aceptación.

    Pieza Qué declara Quién la hace cumplir
    Contrato Lo que se construye, en cláusulas que una máquina puede rechazar AGENTS.md para lo permanente del repo + la spec de la tarea
    Carril Por dónde el agente no puede salirse: qué no toca, qué no instala, qué asserts no modifica Límites de escritura declarados por escrito
    Veredicto Si está bien o no, en dos estados y ninguno más El harness de verificación: build, tipos, lint, tests y un comando por criterio de aceptación

    Lo que revisas después no es el diff. Es el veredicto y el contrato.

    Cómo se monta cada pieza, con el AGENTS.md entero y los dos bucles de verificación, lo tengo desarrollado en el post del método. Aquí me interesa lo otro: por qué esto funciona.

    En qué se basa el método para verificar código generado por IA

    Nada de esto lo he inventado yo. Son tres ideas viejas que la IA no rompió, solo hizo urgentes.

    1. Una especificación que no puede fallar es un comentario

    En 1986 Bertrand Meyer metió los contratos dentro del lenguaje Eiffel: precondiciones, postcondiciones, invariantes. La idea era que la especificación de una función dejara de vivir en un documento y pasara a vivir en el código, con una propiedad nueva — que el programa revienta cuando se incumple.

    Esa es la línea que separa documentar de obligar. La misma que ya conoces entre un README que pide formatear antes de commitear y un hook que no te deja commitear sin formatear.

    Tu spec para el agente está casi entera del lado equivocado de esa línea. Coge la última que escribiste y cuenta cuántas de sus líneas podría rechazar una máquina. Suelen ser tres de veinte. Las otras diecisiete son intenciones: orientan al modelo, no rechazan nada, y por eso tu spec no te frenó ni un bug.

    Un contrato no es una spec mejor escrita. Es una spec que puede decir que no.

    Esa conversión —coger tu spec y pasar sus líneas a cláusulas que se pueden incumplir— la tienes entera en el ebook gratuito de Revisión por Contrato, con el código puesto para que lo copies.

    2. Quien produce no puede ser quien juzga

    En cualquier oficio donde el resultado importa, esto es tan obvio que ni se discute. El que lleva las cuentas no es el que las audita. El que escribe el paper no es quien lo revisa.

    En tu repo lleva meses pasando lo contrario y no lo has mirado: el agente tiene permiso de escritura sobre la cosa que lo verifica.

    Los tests son ficheros. El linter se configura con un fichero. El workflow de CI es un fichero. Todo está dentro de su radio de acción. Y cuando le pides que los tests pasen, tocar el assert es un camino perfectamente válido hacia lo que pediste — más corto que arreglar el código, de hecho.

    No hace trampas. Cumple el objetivo por la ruta más barata, que es exactamente para lo que está optimizado.

    Si el verificado puede editar al verificador, no tienes verificación. Tienes teatro. Y de ahí sale el carril, que no es una regla de buenas maneras: es la separación de poderes de tu repositorio.

    3. Llevamos cuarenta años sacando comprobaciones de la cabeza del humano

    El compilador quitó una clase entera de errores que antes se cazaban leyendo. El type checker quitó otra. El linter quitó las discusiones de estilo de las revisiones de código. El CI quitó el "en mi máquina funciona".

    Cada salto de productividad real de esta profesión ha sido el mismo movimiento: coger una comprobación que hacía una persona cansada y dársela a un proceso que no se cansa.

    La Revisión por Contrato no es una idea nueva. Es ese mismo movimiento aplicado al último sitio donde todavía no lo habíamos hecho: comprobar que el código generado hace lo que se pidió.

    Y aquí está el error de época, el que veo en casi todos los equipos: creer que la IA también puede hacer esa parte. Poner un segundo agente a revisar al primero se siente productivo, pero un modelo probabilístico revisando a otro modelo probabilístico no te da un veredicto — te da una segunda opinión, más larga y con la misma naturaleza. La IA genera. Verificar lo hace algo determinista, que sale con código cero o distinto de cero.

    Por qué esto sí resuelve el cuello de botella

    Tres razones, y la tercera es la que me convenció.

    Tu revisión deja de escalar con el tamaño del diff. Hoy revisas ochocientas líneas porque el agente escribió ochocientas. Con contrato revisas cuarenta líneas de contrato y un veredicto, y esas cuarenta líneas no crecen cuando el agente escribe el doble. Rompes el vínculo entre lo que produce la máquina y lo que consume tu atención, que es literalmente la definición de desatascar un cuello de botella.

    El error cambia de sitio y de dueño. Un fallo que detecta el bucle corto a los veintiocho segundos lo arregla el agente, casi siempre solo, y no te enteras. El mismo fallo dentro de una pull request cuesta tu contexto, tu tarde y a veces tu fin de semana. No es que haya menos errores: es que dejan de ser tuyos.

    Y es la única pieza que mejora cuando el modelo mejora. Esta es la buena. Sin verificación, un agente el doble de rápido te dobla la cola de revisión — la mejora del proveedor se convierte en trabajo tuyo. Con verificación, un agente el doble de rápido entrega el doble, porque el harness absorbe el aumento sin pedirte más atención. Es la diferencia entre que los próximos dos años de avances te lleguen como regalo o como factura.

    Lo que no resuelve

    Sería raro que te vendiera esto sin decirte dónde se acaba.

    El contrato comprueba que el código hace lo que pediste. No tiene ni idea de si pediste lo correcto.

    Tampoco te dice si el nombre de ese servicio encaja con el lenguaje del dominio, si la solución es proporcionada al problema, o si acabas de meter la tercera forma distinta de hacer lo mismo en el mismo repo. Eso sigue siendo trabajo humano y lo va a seguir siendo.

    Lo cual, si lo piensas, es una noticia excelente. Ese trabajo — decidir qué se construye y si tiene sentido — siempre fue el nuestro. Lo que nos habíamos autoimpuesto era el otro: leer ochocientas líneas buscando un null.

    La prueba de una línea

    Si quieres saber en treinta segundos si tienes un contrato o un deseo, coge el último criterio de aceptación que escribiste y hazte esta pregunta:

    ¿Puedo escribir algo que compruebe esto sin mí?

    Si la respuesta es sí, es una cláusula. Si es no, es una intención. Y tu tiempo de revisión es, casi exactamente, la suma de tus intenciones.

    Empieza por ahí. Una sola línea de una sola spec, convertida en un comando que devuelve cero o distinto de cero. Es media hora y ya lo notas en la siguiente tarea.

    Cuando quieras el sistema completo — las cinco secciones del AGENTS.md, los dos bucles y los límites, con el porqué de cada línea — está en el ebook gratuito de Revisión por Contrato. Treinta páginas, sin coste.

    Si lo que quieres es montarlo entero de una sentada, sobre un Issue de verdad y hasta la pull request verificada, ese camino completo es el workshop de SDD + Agentic Engineering. Tres horas on-demand, nueve módulos, y sales con tu harness montado en tu repo, no con apuntes.

    Y si prefieres verlo antes de leer nada, esto lo monto en directo cada cierto tiempo: webinar de Revisión por Contrato. Cincuenta y cinco minutos sobre una feature real — el agente entrega, la verificación falla, y en pantalla se ve qué cláusula rompió. Gratis, y la próxima fecha está en la página.

    La parte de cómo se escribe la spec de cada tarea, con más profundidad, la tienes en el libro de Spec-Driven Development.

    El cuello de botella no se mueve solo. Pero se mueve.

    Preguntas frecuentes

    ¿Qué es exactamente la Revisión por Contrato?

    Un método para verificar código generado por IA sin leerlo entero. Tiene tres piezas: un contrato con cláusulas que una máquina puede rechazar (no "debe ser rápido", sino "p95 < 200 ms en el CI"), un carril que declara por escrito dónde el agente no puede escribir, y un veredicto emitido por comandos ejecutables con dos estados posibles. Lo que revisa la persona después es el veredicto y el contrato, no el diff completo.

    ¿Por qué un modelo mejor no te ahorra verificar código generado por IA?

    Porque un modelo mejor sube el porcentaje de aciertos, no elimina la obligación de comprobar. Con un 97% de acierto sobre ochocientas líneas te quedan veinticuatro líneas malas y ninguna indicación de cuáles son, así que sigues teniendo que revisarlas todas.

    ¿No puedo poner otro agente a revisar el código del primero?

    Ayuda como segunda opinión, no como veredicto. Un modelo probabilístico revisando a otro modelo probabilístico produce texto plausible, no un resultado binario y reproducible. La verificación tiene que apoyarse en algo determinista — build, tipos, lint, tests, criterios de aceptación con un comando detrás — precisamente porque no cambia según cómo venga el día.

    ¿Qué relación tiene la Revisión por Contrato con Design by Contract?

    Es la misma idea de Bertrand Meyer (Eiffel, 1986) sacada de la función y aplicada al agente. Design by Contract mete precondiciones, postcondiciones e invariantes dentro del lenguaje para que el programa reviente cuando se incumplen. La Revisión por Contrato hace lo mismo un nivel por encima: escribe los criterios de aceptación de la tarea en cláusulas que un comando puede rechazar, para que el fallo lo cace el harness de verificación y no tus ojos a las once de la noche.


    Por Bezael Pérez — Developer senior con más de 15 años de experiencia y fundador de Dominicode.

  • Arquitectura de código generado con IA: la Casa Winchester

    Arquitectura de código generado con IA: la Casa Winchester

    Hace tres semanas abrí un repo que llevaba cuatro meses construyendo casi entero con agentes. Buscaba una función para formatear fechas.

    Encontré tres.

    formatDate en src/utils/date.ts. toDisplayDate en src/lib/format.ts. Y humanDate en src/shared/helpers/dates.ts. Las tres hacían lo mismo. Las tres estaban bien escritas. Las tres tenían tests. Ninguna estaba rota.

    Ese es el problema de la arquitectura de código generado con IA: no se rompe. Se desparrama. La arquitectura de código generado con IA es la forma que toma un repositorio cuando la mayor parte del código la escribe un agente y no una persona: cada pieza es correcta por separado, pero nadie sostiene el conjunto en la cabeza. El CI sigue verde mientras el repositorio se convierte en otra cosa.

    Se ha hablado mucho del "Deep Blue" — el término que se acuñó en el podcast Oxide and Friends, con crédito principal a Adam Leventhal y con Simon Willison en ese mismo episodio, que después lo difundió en su blog: esa mezcla de desánimo y vértigo existencial que sienten muchos developers ante los LLM. Este post no va de eso.

    Va de lo que le está pasando a tu repositorio ahora mismo, mientras tú lo miras.

    Qué es la Casa Winchester y por qué se parece a tu repo

    La Casa Winchester, en software, es el modelo que describe un repositorio construido a base de decisiones correctas tomadas de una en una, sin que nadie sostenga el plano general: cada pieza está bien hecha y el conjunto no tiene sentido. El nombre viene de una mansión real, y encaja con lo que produce hoy un agente de codificación.

    Sarah Winchester construyó durante décadas una mansión en San José, California. Sin arquitecto director y sin plano general. Cada obra se hacía bien: buena carpintería, buenos materiales, habitaciones perfectamente terminadas.

    El resultado tiene escaleras que acaban en el techo y puertas que abren al vacío.

    Ninguna decisión individual fue estúpida. Falló el conjunto. Nadie tenía en la cabeza la casa entera, así que nunca llegó a ser una casa: fue la suma de muchas obras correctas.

    Abre tu repo generado con agentes y busca ese patrón. No busques bugs. Busca escaleras que no llevan a ningún sitio.

    El tercer modelo: ni catedral ni bazar

    En 1997 Eric S. Raymond presentó La catedral y el bazar, el ensayo que definió los dos modelos con los que llevamos treinta años pensando el software. La catedral: planificada, cerrada, con arquitectos que controlan la forma. El bazar: abierto, caótico en la superficie, ordenado por muchos ojos mirando.

    Drew Breunig propuso en marzo de 2026 un tercero: la Casa Winchester. Su tesis es incómoda y precisa: "AI is making code cheap and kicking off a new era filled with idiosyncratic, sprawling, cobbled-together software".

    La clave no es que la IA escriba mal. Es esta otra frase suya: "Feedback hasn't gotten cheaper; the 'eyeballs' that guided the software developed by the bazaar haven't caught up to AI". El código bajó de precio. Todo lo demás —incluidos los ojos que Raymond puso en el centro del bazar— cuesta exactamente lo mismo que antes.

    Y remata: "There is only one source of feedback that moves at the speed of AI-generated code: yourself".

    Ahí está el problema entero. La revisión de un compañero, el diseño de una API, la discusión sobre si esto merece ser un módulo nuevo: todo eso sigue a velocidad humana. Lo único que se aceleró fue la producción.

    La entropía arquitectónica es la degradación progresiva de la forma de un repositorio —duplicación semántica, abstracciones sin uso, patrones incoherentes— sin que aparezca un solo fallo funcional. No es un problema de calidad de código: es un problema de caudal de feedback. Tu repo se desparrama porque el código llega más rápido de lo que nadie puede juzgarlo.

    Una catedral no se defiende sola: necesita a alguien mirando. El bazar tampoco, porque funcionaba gracias a que se leía despacio. Cuando el que escribe se acelera un orden de magnitud y el que lee sigue exactamente igual, no te queda ni catedral ni bazar. Te queda una casa con escaleras al techo.

    Las 4 señales de que tu arquitectura de código generado con IA ya es una Casa Winchester

    Esto no se detecta leyendo. Se detecta midiendo. Cuatro señales, cada una con su forma de verla hoy mismo.

    # Señal Cómo la mides
    1 La misma utilidad con tres nombres distintos Censo de exports con rg + jscpd
    2 Abstracciones con un solo consumidor Grafo de madge + in-degree
    3 Código muerto que nadie borra knip --reporter compact
    4 Patrones incoherentes entre sesiones Conteo de librerías rivales cruzado con git log

    1. La misma utilidad con tres nombres distintos

    Es la señal madre. El agente no encontró tu helper porque no estaba en su contexto, así que escribió otro. Correcto, con tests, y duplicado.

    # Censo de funciones exportadas: los duplicados semánticos saltan a la vista
    rg -o --no-filename 'export (?:async )?(?:function|const) (\w+)' -r '$1' src \
      | sort | uniq -c | sort -rn | head -30
    
    # Duplicación literal de bloques
    npx jscpd src --min-lines 5 --min-tokens 60 --reporters console
    

    El censo de nombres rinde más de lo que parece. Cuando ves formatDate, toDisplayDate y humanDate seguidos en la misma lista, el diagnóstico es inmediato.

    2. Abstracciones con un solo consumidor

    El agente te construye un UserRepository, un NotificationService y un PaymentGateway porque son buenas prácticas. Luego resulta que cada uno se usa exactamente desde un sitio.

    Una abstracción con un consumidor no es arquitectura. Es una capa de indirección que te cobra peaje cada vez que lees el código.

    npx madge --extensions ts,tsx --json src > deps.json
    

    Si tu repo usa path aliases (@/…), añade --ts-config tsconfig.json o madge no resolverá esos imports y el grafo saldrá incompleto — con lo que el in-degree te mentirá.

    Y un script de veinte líneas que cuenta cuántos módulos importan a cada módulo:

    // scripts/in-degree.mjs
    import { readFileSync } from 'node:fs'
    
    const graph = JSON.parse(readFileSync('deps.json', 'utf8'))
    const inDegree = new Map(Object.keys(graph).map((file) => [file, 0]))
    
    // Los tests no cuentan como consumidor: si el único importador de un módulo
    // es su test, ese módulo tiene cero consumidores de producción, no uno.
    for (const [file, deps] of Object.entries(graph)) {
      if (file.includes('.test.') || file.includes('.spec.')) continue
      for (const dep of deps) {
        inDegree.set(dep, (inDegree.get(dep) ?? 0) + 1)
      }
    }
    
    const suspects = [...inDegree]
      .filter(([file, count]) => count === 1 && !file.includes('.test.'))
      .map(([file]) => file)
      .sort()
    
    console.log(`Módulos con un único consumidor: ${suspects.length}`)
    console.log(suspects.join('\n'))
    

    Ejecútalo con node scripts/in-degree.mjs. Esa lista es tu deuda de indirección con nombres y apellidos.

    3. Código muerto que nadie borra

    Un agente borra cuando se lo pides. Nunca por iniciativa propia, porque borrar es arriesgado y su incentivo es que la tarea pase. Así que el código viejo se queda ahí, acumulándose y ensuciando el contexto de la siguiente sesión.

    npx knip --reporter compact
    

    knip te da ficheros, exports y dependencias que nadie usa. Apunta el número de hoy en algún sitio del repo. Si dentro de un mes ha subido, ya tienes tu métrica de entropía.

    4. Patrones incoherentes entre sesiones

    Esta es la más silenciosa. El módulo que escribiste en junio usa fetch a pelo. El de julio usa TanStack Query. El de agosto se trajo axios porque el agente decidió que era lo estándar.

    for p in "axios" "fetch(" "@tanstack/react-query" "HttpClient"; do
      printf "%-24s %s\n" "$p" "$(rg -l --fixed-strings "$p" src | wc -l)"
    done
    

    Si más de una fila devuelve un número mayor que cero, tienes dos maneras de hacer lo mismo conviviendo en el repo. Cruza el resultado con git log --diff-filter=A --format='%ad' --date=short -- <fichero> y verás que cada patrón corresponde a una tanda distinta de trabajo.

    Escaleras que dan al techo: por qué se degrada la arquitectura de código generado con IA

    Tres causas, y ninguna es "la IA escribe mal".

    El agente empieza cada sesión con amnesia parcial. No lee tu repo entero: lee lo que le cabe en la ventana y lo que sabe buscar — el mismo mecanismo que provoca el context drift. Si tu helper de fechas no aparece en esa muestra, para el agente no existe. Y lo que no existe, se escribe.

    Escribir se volvió más barato que entender. Esto siempre fue verdad, pero antes tecleabas tú, y el coste de escribir 200 líneas te empujaba a reutilizar. Esa fricción desapareció. Hoy reutilizar exige buscar, leer y decidir; crear exige una frase. El camino de menor resistencia lleva al código nuevo.

    El CI que ya tienes no ve nada de esto. Ningún test se pone rojo porque tengas tres formas de formatear fechas. Ningún linter falla porque una capa tenga un solo consumidor. Tus tests miden comportamiento; la entropía es un problema de forma. Es un punto ciego distinto del que conté en los 5 fallos del código generado por IA que un code review no puede ver: allí el diff esconde el fallo, aquí no hay fallo que esconder. Por eso el repo se degrada durante meses con el pipeline en verde.

    Aquí mucha gente responde con más proceso humano: más revisión, más reuniones de arquitectura. No funciona, y Breunig ya te dijo por qué: tú eres el único feedback que va a la velocidad del código, y tú no escalas.

    La respuesta tiene que ir a la misma velocidad que el problema. Es decir: automática.

    Guías y sensores: el plano que le falta al agente

    Birgitta Böckeler publicó el 2 de abril de 2026 en martinfowler.com un artículo sobre harness engineering con la formulación más clara que he leído del asunto. El harness engineering es la disciplina de diseñar todo lo que rodea al modelo —contexto, herramientas, verificaciones y bucles de corrección— para que el agente necesite menos supervisión humana. Su punto de partida: Agent = Model + Harness. El modelo no lo controlas. El arnés agéntico sí, y es tuyo entero.

    El arnés tiene dos mitades.

    Guías (feedforward). Fijan expectativas antes de que el agente actúe. Suben la probabilidad de que acierte a la primera. Documentación de arquitectura, convenciones, instrucciones de arranque.

    Sensores (feedback). Observan la salida después y permiten autocorrección. Böckeler insiste en un detalle que casi todo el mundo se salta: los sensores deben estar "optimised for LLM consumption" — mensajes que le digan al agente qué hacer, no solo qué falló.

    Y cada mitad puede ser computacional (determinista y rápida: tipos, lint, tests, build; milisegundos y resultado fiable) o inferencial (semántica: revisión por LLM, LLM-as-judge; más lenta, más cara y no determinista).

    Guías (antes de actuar) Sensores (después de actuar)
    Computacional (determinista, ms) Tipos, esquemas, plantillas, AGENTS.md con el mapa del repo tsc, ESLint, tests, knip, jscpd
    Inferencial (semántico, lento y caro) How-tos y ejemplos escritos para consumo del LLM Revisión por LLM en el PR, LLM-as-judge

    La conclusión que saco de su artículo es la parte que importa: ninguna mitad vale sola. Solo sensores y tienes un agente que repite siempre los mismos errores. Solo guías y tienes un agente que memoriza reglas sin enterarse nunca de si funcionaron.

    La guía: tu fichero de instrucciones no es un style guide

    El error más común en AGENTS.md o CLAUDE.md es llenarlo de preferencias de formato. Eso ya lo hace Prettier.

    La guía debe contener lo que el agente no puede deducir mirando un fichero suelto: dónde vive cada cosa, quién puede importar a quién y qué existe ya.

    MAPA DEL REPO — no crees carpetas de primer nivel sin preguntar
    
    - `src/domain/`  — tipos y reglas de negocio. No importa NADA de `src/infra/`.
    - `src/infra/`   — HTTP, DB, colas. Implementa los puertos de `src/domain/`.
    - `src/app/`     — casos de uso. Único sitio que orquesta domain + infra.
    - `src/shared/`  — fuente ÚNICA de fechas, dinero y formateo de strings.
    
    ANTES DE ESCRIBIR CUALQUIER UTILIDAD NUEVA
    
    Ejecuta esto y lee la salida. Si algo cubre el 80% del caso, extiéndelo:
    
        rg -n "export (async )?function" src/shared
    
    REGLAS DURAS
    
    - Una sola librería de fetching: `@tanstack/react-query`. Nada de `axios`.
    - No crees una abstracción con menos de dos consumidores reales.
    - Si un código sobra, bórralo. No lo comentes ni lo marques `@deprecated`.
    
    DEFINICIÓN DE "HE TERMINADO"
    
        pnpm agent:check
    

    Ese último bloque es la bisagra entre la guía y los sensores. Si tu definición de "terminado" es "el agente dijo que estaba", no tienes arnés: tienes fe.

    Si quieres un AGENTS.md ya escrito para copiar y adaptar, lo tienes entero en Revisión por Contrato, un ebook gratuito de 30 páginas donde desarrollo el contrato, el carril y el veredicto que le pones a un agente antes de dejarle tocar el repo.

    Fijar la forma antes de que exista el código es el mismo músculo que entrenas con Spec-Driven Development. Si quieres el método completo, lo desarrollo entero en el libro Spec Driven Development.

    Los sensores computacionales: que el agente se corrija solo

    Un único comando que el agente pueda ejecutar sin pedirte permiso:

    {
      "scripts": {
        "typecheck": "tsc --noEmit",
        "lint": "eslint . --max-warnings 0",
        "test": "vitest run",
        "dead": "knip --reporter compact",
        "dupes": "jscpd src --min-tokens 60 --threshold 1 --reporters console,threshold",
        "agent:check": "pnpm typecheck && pnpm lint && pnpm test && pnpm dead && pnpm dupes"
      }
    }
    

    knip y jscpd son los dos que faltan en casi todos los repos, y son justo los que detectan entropía en vez de bugs. jscpd con --threshold 1 sale con código 1 si la duplicación pasa del 1%, pero solo si añades el reporter threshold: el flag por sí solo no cambia el código de salida. Con los dos juntos, "hay algo duplicado" pasa de ser un texto en consola a una señal que el agente lee y sobre la que puede actuar.

    El sensor que más me ha servido es otro: convertir la dirección de dependencias en una regla de lint cuyo mensaje explique el arreglo.

    // eslint.config.js
    export default [
      {
        files: ['src/domain/**/*.ts'],
        rules: {
          'no-restricted-imports': ['error', {
            patterns: [{
              group: ['**/infra/**', 'axios', 'node:fs'],
              message:
                'domain/ no puede importar de infra/. Define un puerto (interfaz) en ' +
                'src/domain/ports/, impleméntalo en src/infra/ e inyéctalo desde el ' +
                'caso de uso en src/app/. No muevas el fichero: mueve la dependencia.',
            }],
          }],
        },
      },
    ]
    

    Fíjate en el mensaje. No dice "import restringido". Dice qué hacer, en qué orden y con qué carpetas. El agente lo lee, lo aplica y no te interrumpe. Eso es un sensor optimizado para consumo de LLM.

    Dos detalles que te ahorran un rato: export default en eslint.config.js exige "type": "module" en el package.json —o renombrar el fichero a eslint.config.mjs—, y si quieres bloquear también los import type de TypeScript necesitas @typescript-eslint/no-restricted-imports en vez de la regla core. La regla en sí no es más que Clean Architecture convertida en algo que el agente puede ejecutar.

    La misma lógica aplicada a la ejecución del agente la desarrollo en el post sobre guardrails para agentes con acceso a terminal y base de datos, y llevada a testear al propio agente en el de test harness para agentes de IA.

    Los sensores inferenciales: para lo que ningún linter ve

    Hay preguntas que ninguna regla determinista responde. ¿Esta función duplica algo que ya existe con otro nombre? ¿Esta abstracción tiene razón de ser? ¿Este módulo sigue el patrón del resto del repo?

    Eso es trabajo de un revisor LLM en el PR, con un prompt que pregunte por coherencia y no por corrección. La corrección ya la cubren los tipos y los tests. Lo que te falta es alguien que mire la casa entera. Cómo montarlo lo cuento en el post de agentic code review.

    Es más lento y no determinista, sí. Por eso va en el PR y no en cada guardado.

    Qué revisar en tu repo esta semana

    Cinco cosas, por orden. Ninguna te lleva más de una tarde.

    1. Saca tu línea base. Ejecuta npx knip y npx jscpd src hoy. Apunta los dos números en un fichero del repo con la fecha. Sin línea base no sabes si mejoras o empeoras.
    2. Abre tu AGENTS.md o CLAUDE.md. Si lo que hay dentro es un style guide, reescríbelo como mapa: dónde vive cada cosa y quién importa a quién.
    3. Añade agent:check al package.json y ponlo en la guía como definición literal de "he terminado".
    4. Convierte una regla de arquitectura en lint, con mensaje accionable. Una sola. La dirección de dependencias es la que más rinde.
    5. Aplica la regla de los dos consumidores. Coge la salida del script de in-degree, elige una abstracción que solo se use una vez y bórrala metiendo el código donde se usa. Vas a respirar mejor.

    La conclusión después de cuatro meses generando código con agentes es esta: el agente no tiene criterio arquitectónico, tiene contexto. Si tu criterio no está escrito en la guía y no lo verifica un sensor, para el agente no existe. Y lo que no existe se reinventa cada sesión con un nombre distinto.

    Sarah Winchester tenía dinero, buenos carpinteros y décadas por delante. Le faltó el plano. Tú tienes agentes que escriben más rápido de lo que nadie puede leer. El plano ya no es opcional.

    Si quieres ver el flujo completo funcionando — guías, sensores y agentes dentro de un arnés que aguanta — lo monto paso a paso en el curso Construye con IA: de la idea al producto con Claude Code. Y si prefieres trabajarlo sobre proyectos reales, eso pasa en Dominicode Labs.

    Preguntas frecuentes

    ¿Qué es la entropía arquitectónica en código generado con IA?

    Es la degradación de la forma de un repositorio sin que aparezca ningún fallo funcional: tres funciones que hacen lo mismo con nombres distintos, abstracciones con un único consumidor, código muerto que nadie borra y patrones que cambian según la sesión en que se escribió cada módulo. Se distingue de un bug en que ningún test la detecta: los tests miden comportamiento y la entropía es un problema de estructura. Se mide con herramientas de duplicación (jscpd), de código muerto (knip) y de grafo de dependencias (madge).

    ¿Esto no es simplemente deuda técnica de toda la vida?

    Misma familia, otra dinámica. La deuda técnica clásica la generas tú y la sientes al escribirla: sabes que estás tomando un atajo. Esta la genera un agente que hace las cosas bien en cada tarea individual, así que nunca hay atajo consciente ni sensación de deuda. Se acumula sin fricción y sin señal. Por eso hay que medirla, no intuirla.

    Si trabajo solo, ¿esto me afecta igual?

    Más. El modelo de la Casa Winchester describe precisamente proyectos personales donde el bucle de feedback se colapsa dentro de una sola cabeza. Sin nadie que revise, tu única defensa son los sensores automáticos. Un equipo grande al menos tiene pull requests con humanos delante; tú tienes exactamente lo que hayas automatizado.

    ¿Cuánto debe ocupar el fichero de instrucciones del agente?

    Corto y denso. Si pasa de una pantalla y media, el agente empieza a ignorar partes. Prioriza el mapa del repo, tres o cuatro reglas duras y el comando de verificación. Todo lo que se pueda comprobar con un linter, sácalo del fichero y ponlo como sensor: ahí sí se cumple siempre.

    ¿Los sensores inferenciales sustituyen al code review humano?

    No, lo reordenan. El revisor LLM absorbe el volumen y filtra lo obvio: duplicación, incoherencia de patrones, abstracciones sin uso. Tú te quedas con lo que exige criterio de producto y de negocio. Si intentas leer cada línea que produce un agente, vuelves al cuello de botella del que veníamos.

    Mi repo ya es una Casa Winchester. ¿Reescribo?

    No. Las reescrituras completas con agentes fallan por la misma razón que falló el repo original: mucho código y poco feedback. Congela primero — mete los sensores y la guía para que la entropía deje de crecer. Después ataca una zona por semana, empezando por las utilidades duplicadas: son las más baratas de unificar y las que más contexto sucio limpian para las sesiones siguientes.


    Por Bezael Pérez — Developer senior con más de 15 años de experiencia y fundador de Dominicode.