Volver al blog

Desarrollo guiado por especificaciones con Claude Code

Desarrollo guiado por especificaciones con Claude Code

El desarrollador que escribe "add user authentication" en Claude Code obtiene un resultado diferente cada vez. Tal vez JWT. Tal vez cookies de sesion. Tal vez un flujo OAuth2 completo con refresh tokens y PKCE. El agente no sabe lo que quieres porque no se lo has dicho. Le diste una direccion, no un destino.

Los desarrolladores que veo obteniendo resultados consistentes y entregables de Claude Code comparten un habito: escriben una especificacion antes de entregar trabajo al agente. No una novela. No un ticket de Jira con tres oraciones de contexto. Un documento concreto que define como se ve "terminado" antes de que alguien escriba una linea de codigo.

Esto no es sabiduria nueva. El desarrollo spec-first precede a la IA por decadas. Pero con agentes, el costo de saltarse la spec es mayor y el beneficio de escribir una es mas grande. Un desarrollador humano puede detenerse a mitad de la implementacion y preguntar "espera, te referias a autenticacion por contrasena o SSO?" Un agente elegira uno en silencio y seguira adelante. Para cuando te des cuenta, construyo lo incorrecto, y perdiste 20 minutos revisando codigo que necesita ser descartado.

Este articulo recorre el ciclo de vida guiado por specs que uso con Claude Code todos los dias: como escribir especificaciones contra las que los agentes pueden ejecutar, el checkpoint de plan-antes-del-codigo que atrapa malentendidos temprano, y un protocolo de verificacion mas riguroso que "compila."

Por que "solo construyelo" falla con agentes

Seamos especificos sobre el modo de fallo. Cuando le das a Claude Code una instruccion vaga, tres cosas salen mal:

Suposiciones silenciosas. El agente llena cada hueco en tu spec con sus propias suposiciones. A veces son razonables. A veces no. No sabras en que categoria estas hasta que leas el output. Con instrucciones vagas, terminas leyendo el output con mas cuidado del que te hubiera costado escribir una spec.

Resultados no reproducibles. Ejecuta el mismo prompt vago dos veces y obtendras dos implementaciones diferentes. No solo nombres de variables o formato diferentes. Decisiones arquitectonicas diferentes. Bibliotecas diferentes. Estrategias de manejo de errores diferentes. Si no puedes reproducir el output, no puedes construir un proceso confiable alrededor de el.

La revision se convierte en el cuello de botella. Cuando el agente toma todas las decisiones, tu tienes que verificar todas las decisiones. Un diff de 400 lineas donde entiendes cada eleccion toma 5 minutos de revision. Un diff de 400 lineas donde el agente eligio el esquema de base de datos, la forma de la API, los codigos de error y la logica de validacion toma 30 minutos porque estas reconstruyendo la spec a partir de la implementacion.

La solucion no son mejores prompts. Es adelantar las decisiones que importan a un documento contra el que el agente puede ejecutar.

El ciclo de vida guiado por especificaciones

El flujo de trabajo tiene cinco fases. Cada una tiene una condicion de entrada clara y una condicion de salida clara.

Fase 1: Lluvia de ideas. Exploras el espacio del problema. Cuales son las restricciones? Que enfoques existen? Que has intentado antes? Aqui es donde piensas en voz alta, ya sea solo o con Claude Code en modo conversacional. La condicion de salida es: tienes un enfoque preferido y entiendes las compensaciones.

Fase 2: Revision. Pones a prueba el enfoque. Que podria salir mal? Que casos limite existen? Esto conflictua con algo que ya esta en el codebase? Si trabajas con multiples agentes, aqui es donde un agente de arquitectura o una segunda opinion es valioso. La condicion de salida es: estas seguro de que el enfoque es solido.

Fase 3: Spec. Escribes lo que decidiste. Planteamiento del problema, enfoque propuesto, archivos a modificar, criterios de aceptacion verificables mecanicamente y un plan de pruebas. Este es el contrato. La condicion de salida es: alguien (humano o agente) podria leer esta spec y saber exactamente que construir y como verificarlo.

Fase 4: Implementacion. El agente ejecuta contra la spec. No contra una idea vaga. Contra un documento concreto con criterios testeables. La condicion de salida es: el agente dice que termino y ha publicado evidencia de verificacion.

Fase 5: Verificacion. Tu (o un agente de QA) confirmas que la implementacion coincide con la spec. No "se ve bien" sino "satisface cada criterio de aceptacion." La condicion de salida es: cada criterio esta verificado, y los que fallan regresan a la Fase 4.

La idea clave: las fases 1-3 son baratas. Toman 10-20 minutos para una funcionalidad de tamano medio. La fase 4 toma lo que dure la implementacion. La fase 5 toma 5-10 minutos. Saltarse las fases 1-3 no ahorra 10-20 minutos. Te cuesta el tiempo de revisar, depurar y rehacer trabajo que fue en la direccion equivocada.

Como se ve una buena spec para agentes

Aqui hay una plantilla de spec real. No una user story. No un documento de requisitos de producto. Un documento de trabajo que le dice a un agente exactamente que construir.

## Problem
The filter bar resets when switching workspaces. Users lose their
filter state and have to re-apply filters every time they switch.

## Approach
Persist filter state per-workspace in localStorage. Key the stored
state by workspace database path so filters don't bleed across
workspaces.

## Files to Modify
- lib/local-storage.ts: Add getWorkspaceFilters / setWorkspaceFilters
- components/filter-bar.tsx: Read initial state from localStorage,
  write on every change
- hooks/use-workspace.ts: Trigger filter restore on workspace switch

## Acceptance Criteria
1. Select workspace A, set filters to status=open + type=bug
2. Switch to workspace B. Filters reset to defaults.
3. Switch back to workspace A. Filters restore to status=open + type=bug.
4. Close the browser tab, reopen. Filters for the active workspace
   are still applied.
5. bd list --status=open --type=bug output matches the filtered table.

## Out of Scope
- Server-side filter persistence
- Filter presets / saved filter combinations
- URL-based filter state (query params)

## Test Plan
- Unit test: getWorkspaceFilters returns stored value for matching
  workspace path
- Unit test: setWorkspaceFilters writes correct key format
- Manual test: steps 1-5 from acceptance criteria above

Observa lo que esta spec contiene y lo que no. No explica como funciona localStorage. Claude Code sabe eso. No justifica por que elegimos localStorage en vez de URL params. Eso paso en la fase de lluvia de ideas. Si lista cada archivo que el agente debe tocar, lo que significa que si el agente empieza a modificar archivos fuera de esta lista, eso es una senal de alerta. Incluye una seccion de fuera de alcance, que evita que el agente haga gold plating.

Los criterios de aceptacion son la parte mas importante. Cada uno es una accion concreta con un resultado observable. No "los filtros deberian persistir." Eso es ambiguo. "Cambia al workspace A, verifica que los filtros son status=open + type=bug" es testeable. Un agente puede ejecutar eso. Un revisor de QA puede verificarlo.

El patron plan-antes-del-codigo

Aqui hay un protocolo que atrapa la mayoria de los errores de implementacion antes de que se conviertan en codigo: exigirle al agente que publique su plan de implementacion antes de escribir algo.

En la practica, esto se ve como un comentario estructurado en la tarea. Antes de que el agente abra cualquier archivo, escribe exactamente lo que piensa hacer.

PLAN: Persist filter state per-workspace using localStorage.

1. Add workspace-scoped storage helpers to lib/local-storage.ts:
   - getWorkspaceFilters(dbPath: string): FilterState | null
   - setWorkspaceFilters(dbPath: string, filters: FilterState): void
   - Key format: "beadbox:filters:{dbPath}"

2. Update components/filter-bar.tsx:
   - On mount, read stored filters for current workspace
   - On every filter change, write to localStorage
   - Accept databasePath as prop (currently not passed)

3. Update hooks/use-workspace.ts:
   - On workspace switch, emit event that filter-bar listens for
   - Filter-bar reads new workspace's stored filters on event

4. Threading databasePath:
   - filter-bar needs databasePath from the workspace context
   - Will add it to the WorkspaceProvider context value

Files: lib/local-storage.ts, components/filter-bar.tsx,
       hooks/use-workspace.ts, components/workspace-provider.tsx

Test: Unit tests for storage helpers, manual test of all 5
acceptance criteria.

Esto le toma al agente unos 30 segundos. A ti te toma unos 2 minutos leerlo. Y en esos 2 minutos, puedes detectar problemas que tomarian 20 minutos arreglar despues de la implementacion:

  • Esta el agente tocando archivos fuera de la spec? (Agregar workspace-provider.tsx no estaba en la spec. Eso esta bien o es scope creep?)
  • Tiene sentido el enfoque? (Usar un event emitter para cambios de workspace podria estar sobredimensionado. Un cambio de prop mas simple podria funcionar.)
  • Faltan pasos? (Que pasa con limpiar entradas antiguas de localStorage cuando se elimina un workspace?)

El plan es un checkpoint. Si se ve bien, dile al agente que continue. Si se ve mal, corrige el plan. De cualquier forma, invertiste 2 minutos en vez de 20.

Este es el problema que Beadbox resuelve.

Visibilidad en tiempo real de lo que toda tu flota de agentes esta haciendo.

Pruebalo gratis durante la beta →

La verificacion no es "compila"

El modo de fallo mas comun que veo con Claude Code no es que los agentes escriban mal codigo. Es que nadie verifico el output contra la spec.

El agente dice "DONE." El desarrollador hojea el diff, ve que se ve razonable y hace merge. Dos dias despues alguien descubre que la funcionalidad no maneja el caso limite numero 3 de los criterios de aceptacion. Ahora estas depurando en produccion en vez de atraparlo en el paso de verificacion de 5 minutos.

Verificacion significa revisar mecanicamente cada criterio de aceptacion. No "parece que funciona." No "los tests pasan" (los tests pueden estar mal o incompletos). Cada criterio de la spec recibe una verificacion concreta.

Asi se ve un reporte de finalizacion apropiado:

DONE: Filter bar now persists selected filters across workspace
switches using per-workspace localStorage.

Changes:
- lib/local-storage.ts: Added getWorkspaceFilters/setWorkspaceFilters
  with key format "beadbox:filters:{dbPath}"
- components/filter-bar.tsx: Reads stored filters on mount, writes
  on change. Accepts databasePath prop.
- hooks/use-workspace.ts: Triggers filter restore on workspace switch
  via callback prop.

QA Verification:
1. Open http://localhost:41420, select workspace A
2. Set filters to status=open, type=bug
3. Switch to workspace B via header dropdown
4. Switch back to workspace A
5. Verify filters are still status=open, type=bug
   -> Confirmed: filters restore correctly
6. Close tab, reopen. Filters persist.
   -> Confirmed: localStorage key present, filters applied on mount
7. Run: bd list --status=open --type=bug
   -> Output matches filtered table contents (14 beads)

Acceptance criteria:
- [x] Filters persist across workspace switches (steps 2-5)
- [x] Filters survive browser restart (step 6)
- [x] Filtered view matches bd CLI output (step 7)
- [x] Filters don't bleed between workspaces (step 3: workspace B
      shows defaults)

Unit tests: 3 added (storage read/write/key format). All passing.

Commit: a1b2c3d

La diferencia entre esto y "DONE: Fixed the filter bar" es la diferencia entre un pase de QA de 5 minutos y una investigacion de 30 minutos. Cada afirmacion en el comentario DONE esta respaldada por una verificacion especifica. Cada criterio de aceptacion esta mapeado a un paso de verificacion. El revisor sabe exactamente que se construyo, como se verifico y donde buscar si algo parece raro.

Beads como contenedor de specs

El ciclo de vida que acabo de describir necesita un lugar donde vivir. La spec, el comentario del plan, la implementacion, el reporte de finalizacion, los resultados de verificacion. Todo, adjunto a una tarea, en un solo lugar.

Este es el problema que resuelve beads. Beads es un issue tracker open-source y local-first disenado para exactamente este flujo de trabajo. Cada "bead" es una tarea con una descripcion (tu spec), un hilo de comentarios (planes y reportes de finalizacion), un estado (open, in_progress, ready_for_qa, closed) y metadatos como prioridad, dependencias y asignaciones.

Asi se ve el ciclo de vida guiado por specs en la practica con el CLI bd:

Crea el bead con tu spec:

bd create --title "Persist filter state across workspace switches" \
  --description "## Problem
The filter bar resets when switching workspaces...

## Acceptance Criteria
1. Select workspace A, set filters...
2. Switch to workspace B..." \
  --type feature --priority p2

El agente reclama el trabajo y publica un plan:

bd update bb-a1b2 --claim --actor eng1
bd comments add bb-a1b2 --author eng1 "PLAN: Persist filter state
per-workspace using localStorage.

1. Add workspace-scoped storage helpers...
2. Update filter-bar component...
3. ..."

El agente completa el trabajo y publica un reporte de finalizacion:

bd comments add bb-a1b2 --author eng1 "DONE: Filter bar now persists
selected filters across workspace switches.

QA Verification:
1. Open http://localhost:41420...

Acceptance criteria:
- [x] Filters persist across workspace switches
- [x] Filters survive browser restart
...

Commit: a1b2c3d"

bd update bb-a1b2 --status ready_for_qa

QA lo toma y verifica:

bd show bb-a1b2  # Lee la spec y el comentario DONE
# Ejecuta los pasos de verificacion
bd comments add bb-a1b2 --author qa1 "QA PASS: All 5 acceptance
criteria verified. Filters persist, restore, and match bd CLI output."

Todo el ciclo de vida esta en el bead. La spec es la descripcion. El plan es un comentario. El reporte de finalizacion es un comentario. El resultado de QA es un comentario. En seis meses, si alguien pregunta "como funciona la persistencia de filtros y por que elegimos localStorage en vez de URL params?", la respuesta esta en el hilo de comentarios del bead.

Cuando ejecutas una sola spec a traves de este pipeline, un terminal y bd show es suficiente. Pero este flujo de trabajo muestra su verdadero valor cuando ejecutas multiples specs en paralelo.

Escalando el desarrollo guiado por especificaciones

Imagina el escenario real: tienes tres agentes de Claude Code, cada uno implementando una spec diferente. El Agente A construye una funcionalidad de persistencia de filtros. El Agente B agrega un nuevo endpoint API para estadisticas de workspace. El Agente C corrige un bug de reconexion WebSocket. Cada uno esta en algun punto del ciclo de vida guiado por specs.

En el terminal, necesitarias ejecutar bd list para ver todos los beads activos, luego bd show en cada uno para revisar su estado y leer el ultimo comentario. Son seis comandos para obtener una foto de tres flujos de trabajo en paralelo. Multiplica eso por cinco o diez agentes y estas gastando mas tiempo revisando estado que revisando planes.

Aqui es donde encaja Beadbox. Beadbox es un dashboard en tiempo real que te muestra el estado de cada bead en tu workspace. Cuales specs estan abiertas esperando un agente. Cuales tienen planes publicados que necesitan tu revision. Cuales estan en progreso. Cuales estan listas para verificacion de QA. Todo se actualiza en vivo mientras los agentes escriben comentarios y cambian estados a traves del CLI bd.

No necesitas Beadbox para hacer desarrollo guiado por especificaciones. El CLI maneja todo el ciclo de vida. Pero cuando tienes multiples flujos de trabajo spec-driven en paralelo, poder ver el pipeline de un vistazo en vez de consultar el estado de cada agente individualmente cambia la velocidad con la que puedes revisar planes, desbloquear agentes y detectar trabajo estancado.

Beadbox es gratis durante la beta, y el CLI beads sobre el que funciona es open-source.

Lo que se mantiene verdadero independientemente del tooling

Ya sea que uses beads, GitHub Issues, Linear o archivos de texto plano, el patron guiado por especificaciones funciona porque aborda una asimetria fundamental en como operan los agentes: son rapidos ejecutando y malos juzgando. Cada minuto que inviertes escribiendo una spec clara ahorra multiples minutos revisando output incorrecto, depurando suposiciones silenciosas y rehaciendo trabajo que se desvio.

Los principios:

  1. Define "terminado" antes de "empezar." Los criterios de aceptacion no son opcionales. Son lo unico que hace posible la verificacion.

  2. Los planes son checkpoints, no burocracia. Un comentario de plan de 30 segundos ahorra reescrituras de 20 minutos. Revisa el plan, no el codigo.

  3. La verificacion es un protocolo, no una sensacion. "Se ve bien" no es verificacion. Mapear cada criterio de aceptacion a una verificacion concreta si lo es.

  4. La spec es la unica fuente de verdad. Cuando la implementacion y la spec no coinciden, la implementacion esta mal. Esta regla existe porque los agentes no cuestionaran un mal plan. Lo ejecutaran fielmente y produciran output fielmente incorrecto.

  5. Los limites de alcance previenen la deriva. Una lista explicita de archivos a modificar y una seccion de fuera de alcance evitan que el agente "mejore" cosas que no le pediste mejorar.

La inversion es pequena: 10-20 minutos escribiendo una spec para una funcionalidad que toma una hora implementar. El retorno es grande: resultados consistentes, output revisable y un registro permanente de que se construyo y por que.

Si estas construyendo flujos de trabajo como estos, dale una estrella a Beadbox en GitHub.

Like what you read?

Beadbox is a real-time dashboard for AI agent coordination. Free during the beta.

Share