cote.log
002

Mi sistema de desarrollo agéntico: cómo equilibrar velocidad y control

Me sigue importando el cómo. Así armé un equipo de agentes que sigue mis reglas.

Un día dejé corriendo un script que le pasaba tareas a un equipo de agentes, una tras otra, y me fui a estudiar. Dos horas después había 4 commits, todos con los chequeos en verde, y 67 decisiones escritas esperando mi revisión. No tuve que revertir ninguna.

Pero esto no empezó con los agentes. Empezó con algo que me importa desde antes: entender cómo está hecho lo que construyo.

El cómo, en crisis

En la comunidad siempre se valoró cómo se escribía el código. Bien ordenado, era escalable. Tanto, que diseñábamos para escalar hasta el to-do list personal que iban a usar exactamente una persona y su mamá. Y sobre todo, le hacía la vida más fácil al resto del equipo, al que llegara después o a uno mismo en seis meses, a la hora de revisar, mantener o depurar.

Con el boom de los agentes, eso parece importar cada vez menos. A comienzos de 2025, Andrej Karpathy bautizó el vibe coding: programar entregándose a la IA y olvidándose de que el código existe. Desde entonces hay devs que lo abrazaron por completo, y otros que lo viven casi como un duelo. Simon Højberg lo describe como una crisis de identidad del programador: la pregunta ya no es si la IA escribe bien, sino qué queda de nosotros si el código ya no lo escribimos nosotros. Del otro lado, los que más delegan lo cuentan como una reinvención del oficio, no como una pérdida.

Yo estoy en algún punto intermedio. Delego cada vez más, pero hay una parte del sistema que sé que se puede romper, y esa quiero tenerla a la vista. Lo de la crisis de identidad da para otro post. Por ahora, este es mi intento de no elegir entre velocidad y control.

Lo viví de cerca

Antes del boom de los agentes, entre 2023 y parte de 2025, cuando todavía no daban tan buenos resultados desarrollando software, en mi trabajo teníamos una forma clara de ordenar los proyectos: requerimientos, módulos, servicios, arquitectura y convenciones de código limpio. Funcionaba. Cualquiera del equipo sabía dónde estaba la pieza de código a tocar, y si algo fallaba, podíamos intuir rápido en qué parte del proyecto estaba el error.

Con la llegada de los agentes, y de la orquestación de varios a la vez, mantener el código como uno quería, con los patrones que sigue un equipo, se volvió difícil. Claro, podíamos darle la instrucción general en un prompt: qué archivos tocar, qué rutas revisar, qué patrón seguir. Recuerdo que mi primera forma de trabajar con agentes en proyectos grandes, sin que se perdieran, era escribirles tres párrafos: dónde encontrar lo que debían cambiar, la “inspiración de código” a seguir y el objetivo final.

Luego llegaron las skills, que permitieron definir todos esos patrones que se siguen una y otra vez, y dejar de explicarle al agente cómo hacer las cosas en cada prompt. Porque una cosa es lo que queríamos, pero para algunas personas sigue importando el cómo.

Y esto último es algo que hasta hoy me cuesta un poco asumir. Siempre me ha importado cómo está construido algo: entender por qué una feature o una pieza se hizo de tal forma, o por qué se tomó tal decisión. Con agentes cada vez más potentes, seguirle el rastro a las decisiones de la IA a un nivel tan específico se hace cada vez más difícil.

El debate sobre si hay que revisar el código o no sigue abierto. Yo creo que hay que encontrar un balance, por lo menos por ahora: no revisarlo todo, pero tampoco nada. Tener un flujo que me permita saber qué se hizo, entender cómo se hizo a nivel general y, si quiero bajar al detalle de algo específico, tener un registro donde buscarlo. Ya saben, el famoso cuello de botella actual: human in the loop, que le quita algo de la velocidad desorbitante con la que hoy se puede desarrollar. Por eso lo pongo solo en los puntos que importan.

Así que lo escribí. Y como lo que no está escrito no existe, ni para mí ni para el agente, lo escribí como código.

Qué es un plugin de Claude Code

Lo que escribí se llama bluedot, y es un plugin de Claude Code. Para quien no lo usa, tres ideas:

  • Un plugin es un paquete que se instala en Claude Code y le agrega capacidades: skills (instrucciones que se cargan solo cuando hacen falta), subagentes (agentes con un rol y un modelo propios), comandos (/algo) y hooks (scripts que corren en momentos fijos, como al terminar un turno). La documentación oficial lo explica completo.
  • Se versiona como cualquier dependencia: cada proyecto fija una versión.
  • bluedot es eso: mi forma de construir software, empaquetada.
bluedot-plugins/
├── conventions/La fuente de verdad: 9 secciones, de los principios a cómo cambiar las reglas.
│ ├── pieces/Catálogo de piezas: cuándo existe cada una y qué puede importar.
│ └── profiles/Perfiles de stack (TypeScript, por ahora).
├── decisions/ADRs del propio plugin.
├── plugins/bluedot/Lo que se instala en Claude Code.
│ ├── agents/5 subagentes, cada uno con su modelo.
│ ├── commands//bluedot:task y /bluedot:extract-requirements.
│ ├── hooks/Los gates, al terminar cada turno.
│ ├── scripts/gates.sh y el script que agrega piezas.
│ └── skills/10 skills: las convenciones y una por pieza probada.
├── scripts/Los tests del propio plugin.
└── CHANGELOG.mdUna entrada por versión: qué cambió y cómo actualizar.
Fig. 1El repo de bluedot, recortado a dos niveles. Las convenciones son la fuente de verdad; el plugin es lo que se instala.

Así se declara un subagente: un archivo Markdown con su rol, sus herramientas y su modelo.

---
name: planner
description: Turns a development task into a plan with
  steps per piece, required tests, acceptance criteria
  and "ask before" items. It never writes code.
tools: Read, Grep, Glob, Write, Skill
model: opus
skills:
  - bluedot:conventions
---

Cinco principios

Estos principios los fui extrayendo e iterando en varios proyectos freelance, de trabajo y personales. Tampoco quise abarcar todos los stacks posibles: por ahora cubren aquel con el que desarrollo los proyectos más grandes y que tengo más probado. Si mañana hay que hacer algo en, por ejemplo, Rust, esa forma de hacer las cosas no entra por un proyecto puntual. Tiene que repetirse en varios proyectos para convertirse en regla, y desde ahí se itera. Es el principio 2 aplicado a las propias convenciones.

  1. La lógica vive en un solo lugar. Una pieza llamada core tiene las reglas de negocio; la web, la API o un agente de IA son entradas que la usan, nunca la reimplementan. Cuando algo falla, sé dónde buscar.
  2. Solo lo que hace falta hoy, con forma de crecer. Una pieza se agrega cuando hay un caso de uso real, nunca “por si acaso”. Adiós al to-do list para un millón de usuarios.
  3. Las dependencias van en una sola dirección: entradas → core → base de datos. Los límites los vigila el lint, no la memoria. Ni la mía ni la del agente.
  4. Lo automatizable se automatiza; lo que requiere criterio se decide por escrito, en un registro de decisión (ADR), antes de implementar.
  5. Humanos y agentes siguen las mismas reglas. Una convención que no está escrita no existe.

Así se ve un caso de uso en core: una función que recibe sus dependencias (las inyecta Awilix) y no sabe quién la llama.

export const updateMyProfileUseCase =
  ({ actor, clock, auditLog, accounts }: Deps) =>
  async (command: UpdateMyProfileCommand) => {
    const user = requireAuthenticatedUser(actor);
    const account = await accounts.findById(user, user.id);
    if (!account) throw new UserNotFoundError();
    ensureExpectedVersion(account, command.expectedVersion);

    account.updateProfile(command, clock.now());
    auditLog.stage({ action: 'update', entityId: account.id });
    await accounts.save(user, account);
    return account;
  };

Y esta es la regla que impide que core importe el framework web. Si alguien lo intenta, persona o agente, el lint falla:

// eslint.config.mjs (simplificado)
{
  files: ['packages/core/**'],
  rules: {
    'no-restricted-imports': ['error', {
      patterns: [
        { group: ['hono', 'react', 'next'],
          message: 'core no conoce el framework' },
        { group: ['@app/api', '@app/web'],
          message: 'entradas → core, nunca al revés' },
      ],
    }],
  },
}

Qué es exactamente core, cómo armo la inyección de dependencias y por qué: eso da para otro post.

El equipo de agentes

Cada vez delego más, pero me sigue interesando el cómo de las cosas. Por eso, para delegar más y a la vez estar más seguro de que el trabajo del agente está correcto, o como “yo quiero”, armé uno de los flujos típicos que se ven hoy: un equipo de agentes de desarrollo. Cada uno con su tarea, sus responsabilidades y el modelo que le corresponde según cuánto criterio necesita.

Rol Modelo Por qué
Planificador Opus Decide piezas, skills y qué preguntar
Ejecutor Sonnet Sigue un plan claro; corre en paralelo
Revisor de convenciones Sonnet Compara contra un checklist
Auditor de seguridad Opus Busca lo que no está en el checklist
Tester E2E Sonnet Recorre la pantalla con un navegador

Los coordina un orquestador: el comando /bluedot:task <lo que quiero>.

  1. 01

    Plan

    planner · opus

    Piezas afectadas, skills a cargar y preguntas «ask before».

  2. decido yo

    Apruebo el plan y respondo cada «ask before».

  3. 02

    Implementación

    executor · sonnet · en paralelo

    Un ejecutor por paso del plan.

  4. 03

    Gates

    hook

    Lint, type-check y tests después de cada paso.

    si falla o no corrió → vuelve al ejecutor

  5. 04

    Revisión de convenciones

    convention-reviewer · sonnet

    Contra el checklist de cada pieza.

    máx. 2 rondas por hallazgo

  6. 05

    Seguridadsi aplica

    security-auditor · opus

    Si toca autenticación, permisos, rutas, configuración o dependencias.

  7. 06

    E2Esi aplica

    e2e-tester · sonnet

    Si cambió una pantalla o un flujo.

  8. 07

    Resumen de cierre

    orquestador

    Necesito que decidas / Riesgos / Verificado automáticamente.

  9. decido yo

    Leo el resumen y decido lo pendiente.

Fig. 2El flujo de una tarea. Los agentes hacen el trabajo; yo entro en dos puntos: antes de empezar y al final.

Después de cada paso, y al terminar cada turno, corre un hook con los gates: lint, type-check y tests. La regla más importante salió de QOne: un turno que solo tocó la configuración del repo terminó sin gates y sin avisar. Un gate puede “pasar” sin haber revisado nada, y por eso un gate que no corrió no es un gate verde. Así queda en el hook:

# 0 tareas en un proyecto con piezas: "no corrió", nunca verde.
count_turbo_tasks "${tasks[@]}"
if [ "$task_count" -eq 0 ] && [ "$have_pieces" = "1" ]; then
  report+="${label}: NOT RUN (0 tasks, project has pieces)\n"
  not_run=1
  return
fi

Cómo mantengo el control

Esta es la parte que responde a mi “me importa el cómo”. Son tres mecanismos.

Preguntas “ask before”. El plan se detiene antes de una dependencia nueva, un cambio de esquema o cualquier cosa irreversible. Nada de eso avanza sin que yo responda.

ADRs. Cada decisión que requiere criterio queda escrita: qué se decidió, por qué y qué otras opciones había. Si en tres meses quiero saber por qué algo se hizo así, está ahí. Vale también para las reglas: cualquiera, humano o agente, puede proponer un cambio a las convenciones, pero solo yo lo apruebo. Los agentes nunca cambian las reglas.

  1. 01Un proyecto encuentra un problema.
  2. 02Un agente propone el cambio como ADR.
  3. 03decido yoYo lo apruebo o lo rechazo.
  4. 04Sale una versión nueva de bluedot.
  5. 05Cada proyecto la recibe al subir su versión fijada.

y el próximo proyecto ya parte con la regla

Fig. 3El ciclo de una regla: lo que falla en un proyecto llega a todos, pero solo si yo lo apruebo.

El resumen de cierre. Cada tarea termina igual: en español, con tres secciones y sin listar archivo por archivo. Es lo primero que leo. Uno de ejemplo, adaptado de una tarea real:

**Necesito que decidas**
- La tarea agrega dos variables de entorno. Dales valor en tu
  `.env` local antes de levantar el proyecto: ningún agente
  toca ese archivo.
- Hay un ADR propuesto que se aparta de lo que pedía la tarea.
  Recomiendo aceptarlo.

**Riesgos**
- Una parte solo se probó con la base de datos de tests.
- Falta definir quién custodia el respaldo que propone el ADR.

**Verificado automáticamente**
lint, test:tooling, check-types y test en verde; ningún gate
"no corrió". Convenciones y seguridad: 2 rondas cada una, sin
hallazgos abiertos. E2E: no aplica.
Fig. 4Un resumen de cierre real, tal como llega al terminar una tarea.

Y el versionado: cada proyecto fija una versión exacta del plugin. Nunca se actualiza solo; subir de versión es una tarea planificada, guiada por el CHANGELOG. Si un proyecto necesita desviarse de una regla, lo deja registrado como excepción en un ADR.

Prueba en terreno: QOne

QOne es mi plataforma para coordinar pabellones quirúrgicos, y la estoy migrando a un monorepo nuevo con bluedot. Es la prueba más dura que le he puesto.

Partí por extraer lo que el código antiguo hacía. El comando /bluedot:extract-requirements leyó el módulo de identidad y produjo 31 reglas de negocio, cada una con su archivo y línea de origen; 24 discrepancias entre el código y la documentación, incluidos bugs reales; y 14 tareas listas para ejecutar. Cómo migrar un repo antiguo con agentes merece su propio post.

Después vino la base del sistema: configuración, logs, multi-tenancy, auditoría, cifrado de datos personales. Las últimas cuatro tareas las dejé corriendo solas, con un script que lanzaba /bluedot:task una tras otra. Es el script del principio. En unas dos horas: 4 commits, cada uno con los gates en verde, dos rondas de revisión de convenciones y dos de seguridad. El agente tomó 67 decisiones y las dejó escritas para mi revisión. No tuve que revertir ninguna.

Fig. 5La corrida sola: cuatro tareas, una tras otra.

Lo más valioso fue lo que encontraron los agentes. Tres ejemplos, todos corregidos en la misma tarea y cada uno con su test:

  • la API se conectaba a la base de datos con más permisos de los que debía;
  • se podía escribir un registro de auditoría a nombre de otro usuario;
  • un dato cifrado se podía mover de un cliente a otro.

Y eso no se quedó en QOne. Lo que falló volvió a bluedot como ADR y como regla nueva. Por ejemplo, el aislamiento de datos entre clientes ahora es obligatorio y se prueba con el mismo rol que usa la app. Todo proyecto nuevo parte con eso resuelto: es el ciclo de la figura 3, funcionando.

Qué cambió

Esto me ha permitido mantener desarrollos en paralelo sin perder el control sobre los proyectos: sé qué se hizo, por qué, y dónde mirar si quiero bajar al detalle. Y me dejó tiempo para otras cosas: estudiar, concentrarme en proyectos de robótica e inteligencia artificial, como clasificar galaxias, y en las tareas de negocio e investigación que requieren pensamiento crítico y creatividad.

No está terminado. Sigo aprendiendo sobre desarrollo con agentes, a optimizar el consumo de tokens y a probar otras herramientas de desarrollo con IA. bluedot pasó de mis notas de Obsidian a un plugin versionado en cinco días, y va en la 0.10, no en la 1.0. Cada proyecto nuevo le sigue enseñando algo.

Cada vez delego más. Pero me sigue importando el cómo, y ahora queda escrito.

Si quieres ordenar el desarrollo de tu equipo con agentes, conversemos.

Siguiente registro: cómo migré un repo antiguo con agentes.


Para leer más

← volver al log