M2Anatomía de una spec
2.1 De la plantilla mínima a la spec completa
En el M1 usaste una plantilla mínima de 4 bloques. Funciona para empezar. Pero una spec profesional tiene más capas — no porque sea burocracia, sino porque cada capa elimina una categoría de ambigüedad.
La estructura completa de una spec SDD es:
1. TÍTULO
2. CONTEXTO
3. OBJETIVO
4. COMPORTAMIENTO ESPERADO
5. RESTRICCIONES
6. CASOS LÍMITE (edge cases)
7. CRITERIOS DE VALIDACIÓN
8. NOTAS TÉCNICAS (opcional)
9. FUERA DE ALCANCE
Cada bloque elimina una categoría de ambigüedad. El 9, sin el cual la IA expande el alcance.
Vamos bloque a bloque.
2.2 Bloque por bloque
1. TÍTULO
Una línea. Describe la acción, no el resultado.
❌ Mal: "Autenticación"
✅ Bien: "Implementar login con email/password y redirección post-auth"
El título debe poder leerse en 5 segundos y saber exactamente qué se va a construir.
2. CONTEXTO
Qué existe ahora mismo. El estado del sistema antes de que empieces.
Incluye:
- Stack tecnológico relevante
- Archivos o módulos que van a verse afectados
- Estado actual de la funcionalidad (¿existe algo? ¿está roto? ¿es nuevo?)
- Dependencias externas si las hay
El contexto es lo que evita que la IA asuma cosas que no son ciertas. Si no le dices que usas Next.js 14 con App Router, puede generarte código de Pages Router.
3. OBJETIVO
Por qué se hace esto. Una o dos frases.
No es lo mismo que el comportamiento esperado. El objetivo es la razón de negocio o técnica.
Ejemplo:
- Comportamiento: "El usuario puede hacer login con email y password"
- Objetivo: "Permitir acceso autenticado para proteger rutas privadas del dashboard"
El objetivo ayuda a la IA a tomar decisiones cuando hay ambigüedad. Si sabe por qué existe algo, puede elegir mejor entre dos implementaciones posibles.
4. COMPORTAMIENTO ESPERADO
El núcleo de la spec. Qué tiene que hacer exactamente.
Reglas para escribirlo bien:
- Usa frases en presente: "El sistema muestra X", "El usuario puede Y"
- Define input y output explícitamente
- Describe el flujo paso a paso si hay más de una acción
- Si hay estados (loading, error, success), descríbelos todos
Ejemplo:
- El usuario introduce email y password en el formulario
- Al hacer submit, el sistema valida formato de email (regex estándar)
- Si el formato es inválido, muestra error inline bajo el campo sin submit al servidor
- Si el formato es válido, hace POST a /api/auth/login
- Si la respuesta es 200, redirige a /dashboard
- Si la respuesta es 401, muestra mensaje "Credenciales incorrectas" sin limpiar el email
- Si la respuesta es 500, muestra mensaje genérico "Error del servidor. Inténtalo de nuevo."
Fíjate: cada estado tiene una respuesta definida. No hay "ya veremos qué pasa".
5. RESTRICCIONES
Qué no puede hacer. Qué debe respetar.
Tipos de restricciones:
- Técnicas: "No usar librerías externas de autenticación", "Mantener el patrón de hooks existente"
- De estilo: "Seguir el sistema de design tokens del proyecto", "No añadir clases de Tailwind nuevas"
- De arquitectura: "La lógica de negocio va en /lib, no en el componente"
- De seguridad: "No almacenar el token en localStorage, usar httpOnly cookie"
Sin restricciones, la IA hace lo que le parece más cómodo. A veces bien. A veces no.
6. CASOS LÍMITE (edge cases)
Las situaciones no obvias que hay que manejar. Son los que más fallan en vibe coding porque nadie los piensa de antemano.
Ejemplos para un formulario de login:
- ¿Qué pasa si el usuario hace doble click en submit?
- ¿Qué pasa si pierde la conexión a mitad del request?
- ¿Qué pasa si el email tiene espacios al principio o al final?
- ¿Qué pasa si el campo password está vacío?
Listar edge cases antes de implementar es uno de los gestos más valiosos de SDD. Obliga a pensar en el sistema como un todo, no solo en el happy path.
7. CRITERIOS DE VALIDACIÓN
Cómo se verifica que la implementación es correcta.
Pueden ser:
- Tests unitarios concretos ("dado email inválido → no hace fetch")
- Tests de integración ("POST a /api/auth/login con credenciales correctas → 200 + redirect")
- Criterios manuales si los automáticos no aplican ("Al recargar la página, el usuario sigue logueado")
La regla de oro: si un criterio no es verificable, no es un criterio. "Que funcione bien" no es un criterio. "Que retorne 401 con credenciales incorrectas" sí lo es.
8. NOTAS TÉCNICAS (opcional)
Información técnica adicional que ayuda a la implementación pero no define el comportamiento.
Ejemplos:
- "El endpoint /api/auth/login ya existe, solo hay que conectar el formulario"
- "Hay un hook useAuth en /hooks/useAuth.ts que gestiona el estado global"
- "Ver implementación similar en /features/register para referencia de estilo"
Este bloque ahorra tiempo. Evita que la IA reinvente lo que ya existe.
9. FUERA DE ALCANCE
Qué no entra en esta spec explícitamente. Es tan importante como el comportamiento esperado. Define el límite del trabajo.
Ejemplos:
- "No incluye recuperación de contraseña"
- "No incluye login con Google/GitHub"
- "No incluye rate limiting del endpoint (pendiente para M-456)"
Sin este bloque, la IA puede intentar hacer más de lo que necesitas, o tú puedes acabar expandiendo el alcance sin darte cuenta (scope creep).
2.3 Ejemplo completo de spec
TÍTULO: Implementar filtro por estado en el listado de subastas (gowy.es)
CONTEXTO:
- Stack: Next.js 14, App Router, Supabase (tabla: subastas), TypeScript
- El listado de subastas existe en /app/subastas/page.tsx
- Actualmente muestra todas las subastas sin filtro
- La tabla subastas tiene columna "estado" con valores: activa | finalizada | cancelada
OBJETIVO:
Permitir al usuario filtrar subastas por estado para reducir el ruido visual
y encontrar más rápido lo que le interesa.
COMPORTAMIENTO ESPERADO:
- Encima del listado aparecen 4 botones: Todas | Activas | Finalizadas | Canceladas
- Por defecto está seleccionado "Todas"
- Al hacer click en un filtro, el listado se actualiza sin recargar la página
- El botón activo tiene estilo visual diferenciado (usar clase CSS existente .active)
- Si no hay subastas para el filtro seleccionado, muestra texto "No hay subastas en este estado"
- El filtro seleccionado se mantiene si el usuario navega dentro de la página
RESTRICCIONES:
- El filtrado se hace en cliente, no con nueva query a Supabase
- No añadir nuevas dependencias
- Usar el componente Button existente en /components/ui/Button.tsx
- No modificar la query inicial de carga de datos
CASOS LÍMITE:
- Si subastas es null o undefined, no romper (mostrar estado vacío genérico)
- Si el usuario llega con ?estado=activa en la URL, aplicar ese filtro por defecto
- Si el valor del estado en DB no coincide con ninguno de los 4 valores conocidos, mostrarlo en "Todas"
CRITERIOS DE VALIDACIÓN:
- Click en "Activas" → solo se muestran subastas con estado=activa
- Click en "Todas" → se muestran todas las subastas
- Con 0 subastas activas → muestra "No hay subastas en este estado"
- Con ?estado=finalizada en URL → filtro "Finalizadas" activo al cargar
- Test unitario: filterSubastas(subastas, 'activa') retorna solo las de estado activa
NOTAS TÉCNICAS:
- El tipo Subasta está definido en /types/subastas.ts
- Para el estado visual del botón activo, ver cómo lo hace el componente NavBar
FUERA DE ALCANCE:
- Filtro por categoría (pendiente)
- Filtro por rango de precio (pendiente)
- Persistencia del filtro en localStorage
2.4 Cuánto detalle es suficiente
La regla práctica: una spec está completa cuando puedes dársela a alguien que no sabe nada del proyecto y construiría exactamente lo que tú tienes en la cabeza.
Si hay cosas que "se sobreentienden", escríbelas. La IA no sobreentiende igual que tú.
Dicho esto: no es una novela. Si el comportamiento es simple, la spec es corta. El detalle se proporcional a la complejidad, no al perfeccionismo.
2.5 Spec vs. ticket vs. historia de usuario
| Artefacto | Para qué sirve | Nivel de detalle |
|---|---|---|
| Historia de usuario | Comunicar valor al negocio | Bajo |
| Ticket de Jira/Rally | Gestionar trabajo en equipo | Medio |
| Spec SDD | Instruir a la IA con precisión | Alto |
Una spec SDD puede nacer de una historia de usuario o un ticket. No los reemplaza. Los complementa con la precisión que la IA necesita.
Ejercicio M2
Coge el entregable del M1 (tu primera spec en formato mínimo) y expándela usando la estructura completa de 9 bloques.
Si algún bloque no aplica a tu caso, escribe explícitamente "N/A — [razón breve]". No lo dejes en blanco: el blanco da lugar a dudas.
Cuando termines, compártela en el chat para revisión. El feedback que recibas sobre tu spec es la mejor preparación para el M3.
Entregable M2
Spec completa (9 bloques) de una feature real. Guárdala como: M2-Entregable-SpecCompleta.md
Resumen del Módulo 2
- Una spec completa tiene 9 bloques: título, contexto, objetivo, comportamiento, restricciones, edge cases, validación, notas técnicas y fuera de alcance.
- El comportamiento esperado describe todos los estados, no solo el happy path.
- Los edge cases son los que más fallan en vibe coding — escríbelos antes de implementar.
- Los criterios de validación deben ser verificables. Si no se pueden comprobar, no son criterios.
- El bloque "fuera de alcance" es tan importante como el resto: define el límite del trabajo.