Cómo escribir una SPEC que realmente funcione (SDD, IA y agentes)
Guía práctica para escribir SPECs ejecutables de verdad. Decide antes, evita ambigüedades y trabaja con IA y agentes sin generar legacy desde el minuto cero.

No escribo specs porque me guste documentar. Las escribo porque me cansé de malentendidos, de conversaciones circulares y de tener que reinterpretar decisiones cuando ya hay código, tests y prompts en marcha. Esta es la forma más sencilla que he encontrado de escribir specs que luego sí se pueden ejecutar, por personas y por agentes de IA, sin burocracia y sin ruido.
Este artículo es la continuación natural de la Guía completa de Spec-Driven Development (SDD) con IA. Allí explicaba en rasgos generales qué es esto del SDD y cómo funciona por encima. Aquí voy a algo mucho más práctico: cómo escribir una spec que no se rompa en cuanto alguien la use.
Por qué la mayoría de las specs no funcionan
Te diría que llevo años peleándome con specs malas, pero sería engañarme a mí mismo (y a ti). La realidad es que hasta hace relativamente poco los agentes no eran lo bastante consistentes como para que compensara el esfuerzo de escribir specs “de verdad”.
En 2026, sin embargo, el problema ya no es ese. Las specs suelen fallar por algo más básico: dejan demasiado espacio a la interpretación. Parecen claras cuando las escribe quien lo tiene todo en la cabeza, pero se vuelven ambiguas en cuanto otra persona —o un agente— intenta ejecutarlas.
En el fondo, al agente le pasa lo mismo que nos ha pasado toda la vida a los programadores cuando nos llega documentación vaga y poco definida.
En mi experiencia, cuando una spec sale mal suele haber alguno de estos síntomas:
- Mezcla requisitos con decisiones técnicas
- Usa un lenguaje cómodo, pero impreciso
- Da cosas por entendidas
- No deja claro dónde termina el alcance
- Deja cálculos/límites sin definir (unidades, redondeos, umbrales)
Con IA esto no se arregla. Se magnifica. El agente no te va a preguntar qué querías decir: va a decidir por su cuenta… y como te descuides te genera legacy desde el minuto cero.
Qué intento conseguir cuando escribo una SPEC
Con el tiempo he dejado de pensar en la spec como un documento y he empezado a verla como una herramienta para tomar decisiones antes.
Para mí, una spec funciona cuando pasa una prueba muy simple: al final del desarrollo puedo decir, sin discutir, si se ha cumplido o no.
Cuando no puedo hacerlo, la discusión nunca es sobre el código. Siempre acaba siendo sobre algo que dimos por hecho y nunca dejamos por escrito.
Cuando eso ocurre, casi siempre hay una razón detrás. Normalmente alguna de estas tres cosas no estaba bien cerrada:
- El problema no estaba bien acotado
- El comportamiento esperado no era verificable
- Las exclusiones no estaban explícitas
La estructura que uso (y por qué)
No hay una plantilla mágica. Cada caso necesita su mimo y su contexto.
Lo que sí he aprendido es que hay estructuras que te obligan a pensar mejor, incluso cuando la idea todavía está verde.
La que uso casi siempre separa tres cosas que conviene no mezclar:
- qué problema estamos resolviendo y en qué contexto,
- cómo debería comportarse el sistema desde fuera,
- y cómo sabremos, sin discutir, si está bien hecho.
No es casualidad que estas capas encajen bastante bien con Requirements, Design y criterios de aceptación. No porque haya que seguirlas por dogma, sino porque te fuerzan a tomar decisiones en el orden correcto.
Requirements: bajar la idea a un problema concreto
Esta es la parte menos glamurosa y, con diferencia, la más importante.
Aquí no intento escribir bonito. Intento eliminar ambigüedad.
Cuando empiezo una spec, me obligo a dejar claro:
- Qué problema concreto estamos resolviendo
- En qué contexto real se va a usar
- Qué objetivo queremos alcanzar
- Y, sobre todo, qué queda fuera
Con el tiempo he aprendido que el bloque de No incluye es el que más valor aporta. No porque limite, sino porque evita que otros —humanos o IA— tomen decisiones que nunca deberían haber tomado.
En más de una ocasión, solo rellenar esta parte ya me ha hecho darme cuenta de que la idea original no estaba tan clara como parecía.
Design: describir comportamiento sin decidir implementación
Aquí es donde muchas specs se desvían sin que nadie se dé cuenta.
El objetivo de esta sección no es diseñar el sistema por dentro, sino describir qué debe pasar desde fuera. Qué ocurre en el flujo normal, qué pasa cuando algo falla y qué casos borde merecen atención.
Si al leer esta parte alguien empieza a discutir frameworks, patrones o librerías, suele ser una señal de que la spec está mezclando niveles.
Cuando la escribo bien, esta sección suele tener algo curioso: permite que distintas personas lleguen a soluciones técnicas distintas… y todas sean válidas.
Criterios de aceptación: donde se acaba la discusión
Si tuviera que quedarme con una sola parte de la spec, sería esta.
Los criterios de aceptación son el punto donde el lenguaje deja de ser interpretativo y pasa a ser comprobable. No describen cómo implementar nada. Describen cuándo algo está bien y cuándo no.
Siempre los escribo pensando en una pregunta muy concreta: ¿podría alguien que no estuvo en la conversación decidir si esto cumple la spec?
Por eso evito adjetivos cómodos y me apoyo en estructuras simples del tipo Dado / Cuando / Entonces. No porque sea BDD, sino porque obliga a concretar.
Cuando los criterios están bien escritos, muchas discusiones desaparecen solas.
Un ejemplo completo (y deliberadamente acotado)
Nota importante sobre anonimización: la spec original usada en el proyecto define con precisión contractual la identidad de sincronización de respuestas, el modelo mínimo del fichero y la regla de certeza por formato. En esta versión pública esos detalles se omiten deliberadamente por anonimización, pero en una spec ejecutable real son obligatorios y no negociables.
Para que todo esto no se quede en teoría, voy a usar una spec real que he aplicado en un proyecto de trabajo, reescrita y anonimizada para no exponer detalles internos ni decisiones sensibles.
La versión original era una buena especificación técnica: explicaba bien el flujo, la arquitectura y los componentes implicados. El problema es que, como SPEC en sentido SDD, dejaba demasiadas decisiones implícitas.
Esta es la misma idea, pero escrita como una spec que gobierna decisiones y que puede ser ejecutada sin interpretar ni inventar reglas.
⚠️ A partir de aquí empieza la SPEC
Spec — Importación y procesamiento de resultados de evaluaciones (pensada para trabajar con Junie)
Problema que se quiere resolver
Necesitamos importar resultados de evaluaciones desde sistemas externos y persistirlos de forma consistente, garantizando que:
- los resultados se asocian correctamente a la evaluación correspondiente,
- no se generan duplicados al reimportar información,
- y se preserva la estructura y semántica de las respuestas de los participantes.
Este proceso debe permitir reimportaciones seguras y repetibles sin introducir inconsistencias en los datos existentes.
Contexto de uso
El sistema recibe ficheros generados por plataformas externas que contienen los resultados de una evaluación. Estos ficheros pueden variar en formato, pero siguen una estructura tabular coherente.
El sistema es responsable de interpretar estos datos, transformarlos en entidades de dominio y almacenarlos como fuente de verdad interna.
Objetivo
Dado un fichero de resultados válido y los metadatos mínimos de la evaluación:
- el sistema importa los datos,
- crea o actualiza la evaluación correspondiente,
- y persiste las respuestas de los participantes de forma consistente.
El proceso debe ser idempotente y determinista: importar el mismo fichero más de una vez no debe generar duplicados ni inconsistencias.
Contrato mínimo (abstracto):
- Entradas: fichero de resultados +
evaluation_external_id+ metadatos mínimos (definidos en anexo interno). - Salidas: evaluación persistida con respuestas asociadas.
- Invariantes: no duplicados, orden preservado por participante, mismo estado final tras N reimportaciones.
Alcance
Incluye
- Recepción de un fichero de resultados junto con metadatos básicos de la evaluación.
- Detección y uso del parser adecuado según el formato del fichero.
- Transformación de los datos en entidades de dominio.
- Persistencia de la evaluación y sus respuestas asociadas.
- Sincronización de respuestas en caso de reimportación.
No incluye
- Validación semántica del contenido académico de las respuestas.
- Corrección manual o edición posterior de los resultados.
- Generación de informes o visualizaciones.
- Gestión de autenticación o autorización.
- Manejo avanzado de errores de formato más allá del rechazo del fichero.
Reglas de negocio relevantes
- Una evaluación se identifica de forma única por su identificador externo.
- Las respuestas se asocian a participantes individuales.
- Identidad de respuesta: definida por una clave de sincronización estable (OMITIDO POR ANONIMIZACIÓN).
- Algunas filas del fichero representan valores de certeza o confianza asociados a una respuesta.
- Regla de certeza: determinada por una regla posicional dependiente del formato (OMITIDO POR ANONIMIZACIÓN; ver anexo interno de formatos).
- El orden de las respuestas es relevante y debe preservarse.
- La ausencia de valores de certeza no invalida una respuesta.
Comportamiento esperado
Flujo principal
- El sistema recibe un fichero de resultados y los metadatos de la evaluación.
- Se selecciona un parser compatible con el formato del fichero.
- El fichero se procesa fila a fila, transformando cada una en una respuesta de participante.
- Las respuestas se agrupan por participante.
- Se construye una entidad de evaluación que contiene todas las respuestas procesadas.
- La evaluación se persiste:
- si no existe previamente, se crea,
- si ya existe, se sincronizan sus respuestas.
Reimportación
- Si la evaluación ya existe:
- se actualizan sus metadatos,
- se añaden nuevas respuestas,
- se actualizan las existentes,
- y se eliminan las respuestas que ya no aparecen en el fichero importado.
Atomicidad
- Si cualquier fila falla durante el parseo o la transformación, se rechaza el fichero completo y no se persiste ningún dato.
Criterios de aceptación
- Dado un fichero válido de una evaluación inexistente, cuando se procesa la importación, entonces se crea una nueva evaluación con todas sus respuestas asociadas.
- Dado un fichero válido de una evaluación ya existente, cuando se procesa la importación, entonces no aumenta el número total de respuestas al reimportar el mismo fichero; por participante, el conjunto de respuestas definido por la clave de sincronización es único.
- Dado un fichero con un formato no soportado, cuando se intenta importar, entonces el proceso falla sin persistir ningún dato.
- Dada una fila que cumple la regla de posición definida para certeza, cuando se procesa, entonces la respuesta se marca como valor de certeza.
- Dado el mismo fichero importado N veces, cuando se procesa repetidamente, entonces el estado final es idéntico: mismos valores, mismo orden por participante y mismo número total de respuestas.
Restricciones
- El proceso debe ser determinista: el mismo input produce siempre el mismo resultado.
- La importación debe completarse como una operación atómica.
- El sistema debe ser tolerante a reintentos.
- El tratamiento de datos personales debe cumplir la normativa aplicable.
Notas abiertas
- La definición exacta de los formatos soportados se mantiene fuera de esta spec.
- La gestión de errores detallada se documentará en una spec separada si es necesario.
⚠️ Fin de la SPEC
Descansa un poco, ya hemos terminado con el ejemplo
Cómo uso esta SPEC con Junie (sin convertirla en “prompt engineering”)
Una vez tengo la spec cerrada, no empiezo a improvisar prompts. Con Junie esto es todavía más evidente: el agente vive dentro del IDE, ve el código y tiende de forma natural a completar huecos si no se los cierras bien.
La spec es lo que evita eso. No la uso para inspirar a Junie, sino para limitar su espacio de decisión.
En lugar de escribir un prompt creativo, lo que hago es lanzar una tarea explícita, asumiendo que Junie ya tiene contexto del proyecto.
Esto es, más o menos, el texto que suelo usar:
Tarea: implementar la importación de resultados de evaluaciones
Fuente de verdad:
- La SPEC "Importación y procesamiento de resultados de evaluaciones" es la única referencia funcional válida.
- No infieras reglas que no estén explícitas en la spec.
Contexto:
- Estás trabajando dentro de un proyecto existente abierto en el IDE.
- El objetivo no es rediseñar el sistema, sino implementar exactamente el comportamiento descrito.
Qué espero de ti:
1. Implementar el flujo descrito en la spec sin añadir comportamiento extra.
2. Identificar cualquier ambigüedad que impida implementar un criterio de aceptación.
3. Proponer tests que validen cada criterio de aceptación de forma verificable.
Restricciones:
- No modifiques reglas de negocio sin pedir confirmación explícita.
- No introduzcas nuevos formatos, atajos ni "mejoras".
- Mantén el comportamiento determinista y repetible.
Forma de trabajo:
- Resume primero la spec en pasos operativos.
- Señala posibles puntos conflictivos antes de tocar código.
- Implementa de forma incremental, validando cada criterio.La diferencia clave aquí es que Junie ya ve el código y el entorno, así que cualquier ambigüedad en la spec se traduce directamente en decisiones implícitas en el IDE.
⚠️ Ojo con el contexto global del proyecto
Esta spec define qué debe pasar y cuándo está bien hecho.
No define cómo debe estructurarse el proyecto ni las reglas generales de desarrollo.
Para eso, en proyectos reales, es importante tener artefactos separados y estables (Agents.md, guidelines.md, convenciones de tests, estilo, etc.) donde se deje claro:
- cómo esperamos que el agente trabaje,
- qué estándares debe seguir,
- y qué cosas no debe decidir por su cuenta.
De esta forma, la spec se mantiene centrada en el problema, y Junie no tiene que inferir decisiones globales a partir de código suelto.
Si Junie se equivoca, casi nunca es porque “la IA sea mala”. Es porque la spec no cerró una decisión que dábamos por obvia.
Los errores que veo una y otra vez
Hay fallos que se repiten tanto que casi se han normalizado.
Specs que parecen documentación, specs que intentan diseñar el sistema entero, specs que no dicen nunca qué queda fuera o specs donde los criterios son tan vagos que nadie se atreve a usarlos como referencia.
Cada uno de estos errores merece una explicación en detalle, y por eso los trato a fondo en el artículo específico de errores comunes en Spec-Driven Development.
Checklist final antes de dar una spec por válida
Copia y pega esta lista. Si una respuesta no es un sí claro, la spec no está lista:
- ¿Está definido el input mínimo?
- ¿Están definidos el output y sus invariantes?
- ¿Las identidades / claves están explícitas?
- ¿Errores y atomicidad están definidos?
- ¿Idempotencia y determinismo son verificables?
- ¿Los casos borde están listados?
- ¿El No incluye es explícito?
- ¿Los criterios tienen oráculo medible?
- ¿Nada queda “según el formato” sin referencia a un anexo/spec interna?
- ¿Se puede decidir cumplimiento sin conversación?
Ajustar una spec aquí sigue siendo infinitamente más barato que hacerlo cuando ya hay código, tests o agentes en producción.
Por qué esta spec funciona mejor que una spec puramente técnica
Después de verla entera, la diferencia clave no está en la longitud ni en el formato, sino en qué decisiones deja cerradas y cuáles no.
Esta spec funciona mejor que una spec puramente técnica porque:
- Empieza por el problema y el objetivo, no por la arquitectura.
- Define explícitamente qué entra y qué queda fuera, reduciendo suposiciones.
- Aísla las reglas de negocio importantes, protegiéndolas de cambios accidentales.
- Describe comportamiento esperado sin imponer implementación.
- Incluye criterios de aceptación que permiten decir, sin discutir, si algo cumple o no.
- Puede ser ejecutada por una persona o por un agente sin inventar reglas nuevas.
Una spec técnica describe el sistema. Esta spec gobierna cómo se toman decisiones antes de escribir código.
