Los 14 documentos que tu IA necesita para programar tu app

Que la IA escriba código es lo fácil. Lo difícil es explicarle cómo tiene que funcionar tu producto, cómo se construye y cómo saber que funciona. Estos son los documentos que lo resuelven, con un ejemplo completo de cada uno.

Los prompts para crear la carpeta docs/ entera y mantenerla al día.

Si programas con IA, conseguir que escriba código es lo fácil. Lo difícil es darle el contexto suficiente para que entienda cómo tiene que funcionar tu producto, cómo se construye y cómo saber que de verdad funciona. Lo que no le cuentas, se lo inventa.

Para eso sirven estos documentos. Son catorce, cada uno responde una pregunta que no responde ningún otro, y todos cuentan el mismo proyecto inventado para que veas cómo encajan: Turnio, una app de reservas para peluquerías.

Cómo se organizan

AGENTS.md va en la raíz, porque es lo que los agentes buscan al abrir un proyecto. El resto vive en docs/. Van en tres grupos: qué construyes, cómo se construye y cómo se trabaja.

turnio/
├── AGENTS.md                  ← el agente empieza aquí
├── CLAUDE.md                  ← una línea: @AGENTS.md
└── docs/
    ├── PRD.md                 qué y para quién
    ├── APP_FLOW.md            por dónde se mueve cada usuario
    ├── DESIGN_SYSTEM.md       cómo se ve
    ├── TRD.md                 cómo funciona por dentro
    ├── DATABASE.md            dónde viven los datos
    ├── API.md                 cómo se hablan las piezas
    ├── SECURITY.md            qué no se puede romper
    ├── CODE_STYLE.md          cómo se escribe
    ├── IMPLEMENTATION_PLAN.md en qué orden
    ├── TESTING.md             cómo sabes que funciona
    ├── DEPLOYMENT.md          cómo llega a producción
    ├── DECISIONS.md           por qué es así
    └── PROGRESS.md            dónde lo dejamos
DocumentoResponde aSe escribe
PRD.md¿Qué construimos y para quién?Antes de nada
APP_FLOW.md¿Qué hace el usuario, pantalla a pantalla?Después del PRD
DESIGN_SYSTEM.md¿Cómo se ve y se mueve?Antes de la primera pantalla
TRD.md¿Con qué y cómo se construye?Antes de la primera línea
DATABASE.md¿Dónde viven los datos y quién los ve?Antes de la primera tabla
API.md¿Cómo se piden y se devuelven?Antes del primer endpoint
SECURITY.md¿Qué no se puede romper nunca?Con el TRD
CODE_STYLE.md¿Cómo se escribe aquí?Con el primer código
AGENTS.md¿Por dónde empiezo y qué no toco?Con el primer código
IMPLEMENTATION_PLAN.md¿En qué orden?Antes de la primera tarea
TESTING.md¿Cómo sé que funciona?Con la primera tarea
DEPLOYMENT.md¿Cómo llega a producción?Antes del primer despliegue
DECISIONS.md¿Por qué es así?Cada vez que se decide algo
PROGRESS.md¿Dónde lo dejamos?Al cerrar cada sesión

No hace falta tener los catorce el primer día. Una landing se apaña con PRD, DESIGN_SYSTEM y AGENTS. Un MVP suma APP_FLOW, TRD, DATABASE, IMPLEMENTATION_PLAN y PROGRESS. Los demás llegan cuando hay usuarios y dinero de por medio. Lo importante es que la IA tenga un contexto claro y ordenado en lugar de adivinar.

En cada documento del árbol de ficheros puedes pulsar cualquier otro para saltar a él.

1. PRD.md: qué construyes y para quién

Es el documento del que salen todos los demás. Le dice a la IA qué problema resuelve el producto, para quién, qué entra en la primera versión y, casi más importante, qué no entra.

turnio
PRD.mdPreview
docs › PRD.md
PRD.mdQué y por qué

Documento de producto

Qué construimos, para quién y cómo sabremos que ha funcionado. El resto de documentos cuelga de este.

01Resumen

Producto
Turnio
En una frase
Reservas online para peluquerías, sin llamadas ni WhatsApp.
Qué es
Una web donde el cliente reserva cita en 30 segundos y el negocio ve su agenda llena sin contestar un solo mensaje.
Versión
MVP 1.0
Estado
En construcción · lanzamiento previsto el 15 de noviembre

02Problema

Las peluquerías pequeñas gestionan las citas por WhatsApp y por teléfono. Pierden unas 6 horas a la semana contestando mensajes, y el 15 % de las citas acaban en un «no vino» sin avisar.

03Objetivo

Que un negocio reciba su primera reserva online en menos de 10 minutos desde que se registra.

04Usuarios

  • Dueña del negocio: configura servicios, horarios y cobra la señal.
  • Empleado: ve su agenda del día y marca quién ha venido.
  • Cliente final: reserva desde el móvil, sin crear cuenta.

05Necesidad principal

«Quiero que mis clientas reserven solas y que, si no vienen, por lo menos me quede la señal.»

Marta, peluquería de 3 sillas en Sabadell

06Funcionalidades del MVP

  • Página públicaturnio.app/tu-negocio
  • Reserva onlineServicio, hueco y listo
  • AgendaDía y semana por empleado
  • SeñalCobro con tarjeta al reservar
  • RecordatoriosEmail 24 h antes
  • MétricasReservas y no-shows

07Prioridades

MoSCoW: lo que entra sí o sí, lo que entra si da tiempo y lo que no entra.

PrioridadQuéPor qué
ImprescindibleReserva, agenda, horarios, señalSin esto no hay producto
ImportanteRecordatorios, cancelar y cambiar citaEs lo que baja los no-shows
DeseableVarios empleados, métricasLo pide 1 de cada 3 negocios
FueraApp nativa, fidelización, TPVDespués de validar el MVP

08Fuera de alcance

Lo que la IA no debe construir aunque parezca buena idea.

  • App nativa para iOS o Android
  • Varios idiomas
  • Sincronización con Google Calendar
  • Venta de productos o bonos
  • Chat entre cliente y negocio

09Métricas de éxito

  • 50 negocios activos a los 3 meses
  • El 40 % de sus citas llegan por Turnio
  • No-shows por debajo del 5 %
  • Primera reserva en menos de 10 minutos

10Supuestos y riesgos

  • Supuesto: la clienta acepta pagar 5 € de señal
  • Supuesto: el negocio comparte el enlace en Instagram
  • Riesgo: Booksy y Fresha ya existen; competimos en precio y sencillez
  • Riesgo: la dueña no tiene tiempo para configurar nada

El error más común es no escribir «Fuera de alcance». Sin esa lista, la IA añade lo que le parece buena idea, y en una semana tienes un chat, un programa de puntos y ninguna reserva funcionando.

2. APP_FLOW.md: por dónde se mueve cada usuario

Convierte el PRD en recorridos: qué pantallas hay, en qué orden se pasa por ellas y qué ve el usuario en cada una. Evita que la IA se invente pantallas o se deje el camino de vuelta.

turnio
APP_FLOW.mdPreview
docs › APP_FLOW.md
APP_FLOW.mdDel primer clic a la cita

Flujo de la app

Cómo se mueve cada usuario por el producto: por qué pantallas pasa, qué ve en cada una y qué ocurre cuando algo sale mal.

01Resumen

Plataforma
Web responsive, pensada primero para móvil
Roles
Cliente (sin cuenta) · Dueña · Empleado
Entrada del cliente
El enlace del negocio en su bio de Instagram
Entrada del negocio
turnio.app → Crear cuenta

02Flujo del cliente

  1. 1NegocioAbre la página desde Instagram
  2. 2ServicioElige corte, color o peinado
  3. 3HuecoDía y hora libres
  4. 4DatosNombre, móvil y señal de 5 €
  5. 5ConfirmadaEmail con la cita

03Pantallas clave

  1. Peluquería MartaSabadell · ★ 4,9Corte mujer · 25 €Color · 45 €Reservar
    1NegocioFoto, dirección y servicios
  2. ¿Qué te hacemos?Corte mujer · 45 minCorte y color · 2 hPeinado · 30 minContinuar
    2ServicioPrecio y duración a la vista
  3. Martes 1410:0011:3017:00Elegir 11:30
    3HuecoSolo se ve lo que está libre
  4. Tus datosNombreMóvilTarjeta · señal 5 €Confirmar
    4DatosSin cuenta ni contraseña
  5. ¡Hecho, Laura!Martes 14 · 11:30Corte mujer con MartaAñadir al calendarioCambiar cita
    5ConfirmadaCambiar o cancelar, a un clic

04Flujo del negocio

  1. 1RegistroEmail o Google
  2. 2ServiciosNombre, precio y duración
  3. 3HorarioDías, horas y descansos
  4. 4ComparteEnlace para la bio
  5. 5AgendaLas reservas entran solas

05Mapa de pantallas

Cada ruta de la app y lo que hay en ella.

/                    landing
/[negocio]           página pública
/[negocio]/reservar  servicio y hueco
/reserva/[id]        cambiar o cancelar
/entrar  /registro
/panel               agenda de hoy
/panel/servicios
/panel/horario
/panel/clientes
/panel/ajustes       pagos y equipo

06Estados de cada pantalla

Vacío, cargando y error, escritos antes de programarlos.

PantallaVacíoError
Agenda«Comparte tu enlace para recibir la primera»Reintentar sin perder el día elegido
Huecos«No quedan huecos esta semana» + la siguienteMensaje y volver al servicio
Clientes«Aparecerán con su primera reserva»Reintentar

07Caminos alternativos

  • Otro cliente coge el hueco mientras pagas: vuelves al calendario con un aviso
  • El pago falla: el hueco queda retenido 10 minutos
  • Cancelas con menos de 24 h: pierdes la señal y se te dice antes
  • El negocio cierra un día con citas: se avisa a cada cliente

08Avisos que salen

CuándoA quiénCanal
Reserva hechaCliente y negocioEmail
24 h antesClienteEmail
CancelaciónNegocioEmail y panel
No-showNegocioPanel

Lo que casi nadie escribe son los estados vacío y error y los caminos alternativos. Son justo lo que la IA no se imagina sola, y lo primero que se encuentra un usuario de verdad.

3. DESIGN_SYSTEM.md: cómo se ve y cómo se mueve

Colores, tipografía, espacios, componentes y movimiento, con valores exactos. Con él, cada pantalla nueva se parece a las anteriores; sin él, cada una estrena un gris y un radio distintos.

turnio
DESIGN_SYSTEM.mdPreview
docs › DESIGN_SYSTEM.md
DESIGN_SYSTEM.mdDiseña una vez, repite siempre

Sistema de diseño

Los colores, tipos, espacios, componentes y movimientos que existen. Si no está aquí, la IA no se lo inventa.

01Identidad

Personalidad
Cercana, clara y tranquila
Se parece a
Una agenda de papel bien hecha
Nunca
Degradados morados, emojis en la interfaz, sombras duras

02Colores

Solo estos. Nada de hexadecimales sueltos en el código.

  • Tinta#16151A
  • Coral#FF6B4A
  • Coral suave#FFE8E2
  • Fondo#FAFAF7
  • Borde#E7E5E0
  • Éxito#2F9E55
  • Aviso#E6A100
  • Error#D93C3C

03Tipografía

Inter en toda la app. Nada por debajo de 12 px.

EstiloTamañoLíneaPeso
Título 14044700
Título 22834600
Título 32028600
Cuerpo1624400
Pequeño1420400
Etiqueta1216500

04Espaciado

Escala de 4 px. Si un hueco no está aquí, está mal.

  • 4
  • 8
  • 12
  • 16
  • 24
  • 32
  • 48

05Componentes

Sale de shadcn/ui y se adapta aquí, no en cada pantalla.

ComponenteVariantesRegla
BotónPrimario, secundario, fantasma, peligroUn solo primario por pantalla
CampoNormal, error, deshabilitadoEtiqueta siempre visible, nunca solo placeholder
Selector de huecoLibre, elegido, ocupadoEl ocupado no se enseña, no se tacha
Tarjeta de citaPendiente, confirmada, no vinoEl estado con texto, no solo con color
ModalConfirmar, formularioSolo para lo que no se puede deshacer
AvisoÉxito, errorArriba en escritorio, abajo en móvil, 4 s

06Radios y sombras

TokenValorDónde
radius-sm6 pxCampos, etiquetas
radius-md10 pxBotones, tarjetas
radius-lg16 pxModales, hojas
shadow-card0 1px 2px / 6 %Tarjetas
shadow-pop0 12px 32px / 12 %Menús y modales

07Movimiento

Rápido · 150 ms
Hover, pulsar, cambiar un icono
Normal · 250 ms
Menús, pestañas, abrir un modal
Lento · 400 ms
Paneles y entrar en una pantalla
Curva
cubic-bezier(.22, 1, .36, 1), sin rebotes
Siempre
Respetar prefers-reduced-motion

08Accesibilidad

  • Contraste mínimo 4,5:1 en texto
  • Foco visible en todo lo que se pulsa
  • Zonas táctiles de 44 × 44 px
  • Cada campo con su etiqueta
  • Nada se comunica solo con color

09Iconos y pantallas

Iconos
Lucide, 20 px, trazo 1,5
Móvil
Hasta 640 px · se diseña primero
Tableta
640 – 1024 px
Escritorio
Desde 1024 px · contenido a 1200 px máximo

No basta con la paleta: escribe la regla de cada componente («un solo botón primario por pantalla») y cuánto duran las animaciones. Y si además quieres que no parezca hecha con IA, aquí tienes seis cosas que lo arreglan.

4. TRD.md: cómo funciona por dentro

El documento técnico: stack, arquitectura, estructura de carpetas y requisitos con cifras. Si el PRD dice qué, el TRD dice con qué y cómo.

turnio
TRD.mdPreview
docs › TRD.md
TRD.mdCómo funciona por dentro

Documento técnico

Cómo se construye: el stack, cómo encajan las piezas, los servicios externos y los límites técnicos que no se negocian.

01Stack

  • Next.js 15Web y API
  • TypeScriptEstricto
  • TailwindCon shadcn/ui
  • SupabasePostgres, login y ficheros
  • StripeSeñal y suscripción
  • ResendCorreos
  • VercelHosting y cron
  • PostHogAnalítica
  • SentryErrores

02Arquitectura

Un solo proyecto de Next.js. Sin servidor aparte hasta que haga falta.

  1. 1NavegadorMóvil o escritorio
  2. 2Next.jsPáginas en servidor y rutas /api
  3. 3SupabaseDatos con RLS y login
  4. 4StripePago y webhook de vuelta
  5. 5ResendConfirmación y recordatorio

03Estructura

turnio/
├─ app/
│  ├─ (publico)/[negocio]/   # página y reserva
│  ├─ (panel)/panel/         # zona privada
│  └─ api/v1/                # rutas de la API
├─ components/
│  ├─ ui/                    # shadcn adaptado
│  └─ booking/               # piezas de la reserva
├─ lib/                      # lógica, sin React
├─ supabase/migrations/
└─ docs/

04Una reserva, por dentro

  • La página pide los huecos libres a /api/v1/slots
  • Al elegir, el hueco se retiene 10 minutos en la base de datos
  • Stripe Checkout cobra la señal
  • El webhook de Stripe confirma la reserva: nunca la página
  • Resend manda el correo y un cron diario, los recordatorios

05Requisitos no funcionales

RequisitoObjetivoCómo se mide
Carga en móvilLCP por debajo de 2,5 s en 4GVercel Speed Insights
ReservarMenos de 60 s de principio a finEmbudo de PostHog
Disponibilidad99,5 % al mesMonitor externo cada minuto
Escala500 negocios y 20.000 reservas al mes sin tocar la arquitecturaPrueba de carga antes del lanzamiento
DatosTodo en la UE (Frankfurt)Región de Supabase y Vercel

06Si un servicio se cae

ServicioQué pasa
StripeNo se puede reservar con señal; se avisa en la página
ResendEl correo va a una cola y se reintenta
PostHogNada: nunca bloquea la app
SupabasePágina de mantenimiento

07Restricciones

  • Sin servidor aparte ni microservicios
  • Sin ORM: supabase-js y tipos generados
  • Infraestructura por debajo de 50 € al mes
  • Nada que obligue a un plan de pago antes de tener 20 negocios

Escribe las restricciones. «Sin servidor aparte» o «menos de 50 € al mes» son las frases que impiden que la IA te monte microservicios para una app de reservas.

5. DATABASE.md: dónde viven los datos

Las tablas, cómo se relacionan, quién puede leer y escribir cada una y cómo se cambia el esquema. Es el que más caro sale no tener: un error en la base de datos no se arregla con un despliegue.

turnio
DATABASE.mdPreview
docs › DATABASE.md
DATABASE.mdDatos que no se pierden

Base de datos

Las tablas, cómo se relacionan, quién puede leer qué y cómo se cambia el esquema sin romper producción.

01Motor

Base de datos
Postgres 16 en Supabase, región Frankfurt
Acceso
supabase-js con tipos generados del esquema
Migraciones
SQL plano en supabase/migrations
Fechas
Siempre timestamptz en UTC; se enseñan en la hora del negocio
Dinero
Enteros en céntimos, nunca decimales

02Tablas

TablaCampos claveRelación
businessesslug, name, timezone, deposit_centsTiene servicios, equipo y reservas
staffbusiness_id, user_id, rolePertenece a un negocio
servicesbusiness_id, name, minutes, price_centsPertenece a un negocio
availabilitystaff_id, weekday, starts_at, ends_atHorario de cada empleado
customersbusiness_id, name, phone, emailUno por negocio, sin cuenta
bookingsservice_id, staff_id, customer_id, period, statusEl centro de todo
paymentsbooking_id, stripe_session_id, amount_centsUno por reserva

03Relaciones

businesses 1──n services
businesses 1──n staff 1──n availability
businesses 1──n customers 1──n bookings
services   1──n bookings n──1 staff
bookings   1──1 payments

04Reglas que pone la base, no el código

  • Un empleado no puede tener dos reservas solapadas (restricción de exclusión sobre period)
  • status es un enum: held, confirmed, cancelled, no_show
  • Precios y señales mayores o iguales que 0
  • created_at y updated_at en todas las tablas
  • Las reservas no se borran: se cancelan

05Row Level Security

Activa en todas las tablas. Sin política, nadie ve nada.

TablaQuién leeQuién escribe
businessesTodo el mundo (solo slug, nombre y dirección)Su dueña
servicesTodo el mundoStaff del negocio
bookingsStaff del negocioSolo el servidor
customersStaff del negocioSolo el servidor
paymentsDueña del negocioSolo el webhook

06Convenciones

  • Tablas en inglés, plural y snake_case
  • Claves primarias uuid; foráneas como tabla_id
  • Índice en cada columna por la que se filtra
  • Nada de select('*') en producción

07Migraciones

  • Una migración por cambio, con nombre que lo diga
  • Nunca se edita una migración ya aplicada
  • Se prueba con supabase db reset antes de subir
  • Borrar o renombrar, en dos pasos: añadir, migrar datos, quitar

08Copias

Recuperación
A cualquier punto de los últimos 7 días
Exportación
Semanal, fuera de Supabase
Prueba
Se restaura una copia el primer lunes de cada mes

09Comandos

# Base de datos local con los datos de prueba
supabase start && supabase db reset
# Nueva migración
supabase migration new add_no_show_to_bookings
# Tipos de TypeScript a partir del esquema
supabase gen types typescript --local > lib/database.types.ts

Pon en la base las reglas que no se pueden romper, como que un empleado no tenga dos citas a la vez. Una regla que solo vive en el código se la salta el primer endpoint que se olvida de ella.

6. API.md: el contrato entre las piezas

Cada endpoint con su método, quién puede llamarlo, qué recibe, qué devuelve y cómo falla. Con él, la IA escribe el frontend y el backend sin que se contradigan.

turnio
API.mdPreview
docs › API.md
API.mdUn contrato, no una sorpresa

API

Cada endpoint: qué recibe, qué devuelve, quién puede llamarlo y cómo falla. Todos siguen las mismas reglas.

01Base

URL
https://turnio.app/api/v1
Formato
JSON en la entrada y en la salida
Sesión
Cookie de Supabase; las rutas públicas no la piden
Validación
Un esquema de Zod por ruta, antes de tocar nada
Versiones
/v1 en la URL; lo que rompe algo va a /v2

02Endpoints

MétodoRutaQuiénQué hace
GET/businesses/:slugPúblicoDatos y servicios del negocio
GET/slots?service=&date=PúblicoHuecos libres de un día
POST/bookingsPúblicoRetiene el hueco y abre el pago
PATCH/bookings/:idCliente con enlace o staffCambia la hora
POST/bookings/:id/cancelCliente con enlace o staffCancela
GET/panel/bookings?date=StaffAgenda de un día
POST/panel/servicesDueñaCrea un servicio
POST/webhooks/stripeStripe (firma)Confirma el pago
GET/cron/remindersVercel Cron (secreto)Manda los recordatorios

03Petición

POST /api/v1/bookings

{
  "serviceId": "3f2a…",
  "staffId": "9b1c…",
  "startsAt": "2026-10-14T09:30:00Z",
  "customer": {
    "name": "Laura Gil",
    "phone": "+34600111222",
    "email": "laura@correo.com"
  }
}

04Respuesta

201 Created

{
  "data": {
    "bookingId": "c71d…",
    "status": "held",
    "heldUntil": "2026-10-01T10:10:00Z",
    "checkoutUrl": "https://checkout.stripe.com/…"
  }
}

05Errores

Siempre { error: { code, message } }. El mensaje se puede enseñar tal cual.

CódigoHTTPCuándo
VALIDATION_ERROR422El cuerpo no cumple el esquema
UNAUTHORIZED401Falta sesión
FORBIDDEN403Hay sesión, pero no es tu negocio
NOT_FOUND404No existe o no puedes verlo
SLOT_TAKEN409Alguien ha cogido el hueco antes
RATE_LIMITED429Demasiadas peticiones
INTERNAL500Fallo nuestro; va a Sentry con su id

06Reglas

  • POST /bookings acepta Idempotency-Key: el doble clic no crea dos reservas
  • Listas paginadas con cursor, 50 como máximo
  • Fechas en ISO 8601 y UTC; dinero en céntimos
  • Nunca se devuelven ids de Stripe ni campos internos

07Límites

Rutas públicas
60 peticiones por minuto e IP
Crear reserva
5 por hora e IP
Panel
300 por minuto y usuario

08Webhooks

  • Se verifica la firma antes de leer nada
  • Se responde 200 enseguida y se procesa después
  • Cada event.id se guarda: si llega dos veces, se ignora
  • Eventos: checkout.session.completed y charge.refunded

Define los errores una vez y para todos. Si no, cada endpoint falla a su manera y el frontend se llena de casos especiales.

7. SECURITY.md: lo que no se puede romper

Las reglas de seguridad que el código cumple siempre, lo escriba quien lo escriba. La IA no escribe código inseguro por maldad: lo escribe porque nadie le ha dicho qué proteger.

turnio
SECURITY.mdPreview
docs › SECURITY.md
SECURITY.mdSeguro por defecto

Seguridad

Qué se protege, de quién y las reglas que cumple el código siempre, lo haya escrito una persona o un agente.

01Qué protegemos

  • ClientesNombre, móvil y email
  • PagosLas tarjetas nunca pasan por nosotros
  • NegociosSu agenda y sus clientes
  • SecretosClaves de Stripe, Supabase y Resend

02Autenticación

  • Supabase Auth: enlace mágico y Google
  • Sesión en cookie httpOnly, nunca en localStorage
  • Límite de intentos en login y en enlaces
  • El cliente gestiona su cita con un enlace firmado que caduca
  • Doble factor para las dueñas en la v1.1

03Autorización

  • RLS en todas las tablas, cerrada por defecto
  • El business_id sale de la sesión, nunca del cuerpo de la petición
  • La clave service_role solo en webhooks y cron
  • Cada ruta del panel comprueba que el recurso es de tu negocio

04Secretos

  • .env fuera de git; .env.example sin valores
  • NEXT_PUBLIC_ solo para lo que puede ver cualquiera
  • Claves distintas en preview y en producción
  • Si una clave se filtra, se rota ese mismo día

05Entradas y salidas

  • Todo lo que llega se valida con Zod en el servidor
  • Nunca se pinta HTML que venga de un usuario
  • Subidas: solo imágenes, 2 MB, tipo comprobado
  • Errores genéricos hacia fuera, detalle solo en Sentry

06Cabeceras

En next.config.ts, para todas las rutas.

Content-Security-Policy: default-src 'self'; frame-src https://checkout.stripe.com
Strict-Transport-Security: max-age=63072000; includeSubDomains
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=()

07Datos personales

  • Solo se pide lo necesario para la cita
  • Borrar un cliente a petición en menos de 30 días
  • Nada de datos personales en logs ni en analítica
  • Servidores en la UE, contrato de encargo con cada proveedor

08Dependencias

  • npm audit en cada PR; alto o crítico, no entra
  • Dependabot activado
  • Antes de instalar algo: descargas, último commit y licencia

09Si pasa algo

  1. 1DetectarAlerta de Sentry o aviso
  2. 2ContenerCortar el acceso
  3. 3RotarTodas las claves afectadas
  4. 4AvisarA afectados en 72 h
  5. 5AprenderQué falló y qué cambia

Escríbelas como reglas que se pueden comprobar («el business_id sale de la sesión, nunca del cuerpo»), no como deseos («la app debe ser segura»). La lista completa está en la guía para que no te hackeen.

8. CODE_STYLE.md: una sola forma de escribir

Nombres, carpetas, reglas del lenguaje y del framework, errores y commits. Hace que el código de cinco sesiones distintas parezca escrito por la misma persona.

turnio
CODE_STYLE.mdPreview
docs › CODE_STYLE.md
CODE_STYLE.mdUn proyecto, una voz

Estilo de código

Cómo se escribe el código aquí, para que lo que hace la IA y lo que haces tú parezca escrito por la misma persona.

01Principios

  • Simple antes que ingenioso
  • No se abstrae hasta el tercer uso
  • Borrar antes que añadir
  • Los nombres explican el qué; los comentarios, el porqué
  • Antes de escribir algo, se busca si ya existe

02Nombres

QuéEjemplo
ComponentesSlotPicker.tsx
FuncionescreateBooking()
BooleanosisHeld, hasDeposit
ConstantesHOLD_MINUTES
Carpetas y rutaspanel/servicios
TiposBooking, BookingStatus

03Dónde va cada cosa

Por funcionalidad, no por tipo de archivo. El test, al lado de lo que prueba.

components/booking/SlotPicker.tsx      # lo que se ve
lib/bookings/create-booking.ts         # lo que decide
lib/bookings/create-booking.test.ts    # lo que lo prueba
app/api/v1/bookings/route.ts           # solo valida y llama a lib/

04TypeScript

  • strict activado
  • Sin any: unknown y se comprueba
  • Tipos de la base generados, nunca a mano
  • Uniones de texto en vez de enum

05React

  • Server Components por defecto
  • 'use client' solo con estado o eventos
  • Un componente por archivo
  • Nada de useEffect para calcular lo que ya se sabe

06Errores

  • lib/ devuelve { data, error }, no lanza
  • Nunca un catch vacío
  • Los textos de error, en un solo archivo

07Formato

Formateo
Prettier, al guardar
Reglas
ESLint de Next + import/order
Línea
100 caracteres
Imports
Absolutos con @/

08Antes y después

// ✗ Así no
const d = await fetch('/api/v1/slots?s=' + s).then(r => r.json()) as any

// ✓ Así sí
const { data: slots, error } = await getFreeSlots({ serviceId, date })
if (error) return <SlotsError />

09Commits

feat(bookings): retener el hueco 10 minutos mientras se paga
fix(slots): los huecos salían una hora corridos con el cambio de hora
docs(progress): cierre de la sesión del 29 de octubre

Un ejemplo de «así no, así sí» vale más que diez reglas. La IA imita mejor de lo que obedece.

9. AGENTS.md: por dónde empieza el agente

El único que va en la raíz y el primero que se lee. No repite a los demás: dice cuál leer según la tarea, cómo se trabaja y qué no se toca nunca. Claude Code lee CLAUDE.md y el resto de agentes, AGENTS.md; con una línea que importa uno desde el otro, sirven los dos.

turnio
AGENTS.mdPreview
AGENTS.md
AGENTS.mdEmpieza aquí

Instrucciones del agente

Lo primero que lee cualquier IA que toque el proyecto: qué es, dónde está cada cosa y cómo se trabaja aquí.

01El proyecto

Turnio: reservas online para peluquerías. Next.js 15 y Supabase, desplegado en Vercel. Estamos construyendo el MVP; el estado de hoy está en docs/PROGRESS.md.

02Qué leer antes de tocar nada

No hace falta leerlo todo. Lee lo que toca según la tarea.

Si vas a…Lee
Empezar cualquier sesiónPROGRESS.md e IMPLEMENTATION_PLAN.md
Hacer o cambiar una pantallaAPP_FLOW.md y DESIGN_SYSTEM.md
Tocar tablas o consultasDATABASE.md y SECURITY.md
Crear o cambiar un endpointAPI.md y SECURITY.md
Elegir una librería o un servicioTRD.md y DECISIONS.md
Subir algo a producciónDEPLOYMENT.md y TESTING.md

03Cómo trabajas

  1. 1SituarteLee PROGRESS.md
  2. 2PlanExplica qué vas a hacer y espera el sí
  3. 3Cambio pequeñoUna tarea, una rama
  4. 4ComprobarTests, lint y móvil
  5. 5ApuntarActualiza los docs

04Siempre

  • Reutiliza lo que ya existe antes de crear nada
  • Sigue CODE_STYLE.md aunque tú lo harías distinto
  • Di en qué archivo y línea está lo que afirmas
  • Si un doc contradice al código, avisa: no elijas tú

05Nunca

  • Instalar una dependencia sin preguntar
  • Editar una migración ya aplicada
  • Desactivar RLS, un test o el lint para que algo pase
  • Hacer push a main
  • Inventarte una API o una opción que no has comprobado

06Pregunta antes de

  • Borrar datos o archivos
  • Cambiar el esquema de la base
  • Tocar el login, los pagos o los permisos
  • Añadir un servicio externo

07Comandos

npm run dev          # localhost:3000
npm run lint
npm run test         # Vitest
npm run test:e2e     # Playwright
supabase db reset    # base local limpia

08Terminado significa

  • Compila sin avisos
  • Tests y lint en verde
  • Probado a 375 px de ancho
  • Docs actualizados en el mismo commit
  • Sesión apuntada en PROGRESS.md

09CLAUDE.md

Claude Code lee CLAUDE.md; Codex, Cursor y los demás, AGENTS.md. Uno importa al otro y no se duplica nada.

# CLAUDE.md
@AGENTS.md

La tentación es meterlo todo aquí. Si AGENTS.md pasa de dos pantallas, el agente lo lee en diagonal. Que dirija, no que explique.

10. IMPLEMENTATION_PLAN.md: en qué orden

El proyecto partido en fases y tareas numeradas, cada una con lo que tiene que pasar para darla por hecha. La IA hace una tarea cada vez, y tú sabes exactamente cuál.

turnio
IMPLEMENTATION_PLAN.mdPreview
docs › IMPLEMENTATION_PLAN.md
IMPLEMENTATION_PLAN.mdPaso a paso

Plan de implementación

El orden en el que se construye, en fases pequeñas que se pueden probar solas. La IA hace una fase cada vez, nunca el proyecto entero.

01Resumen

Objetivo
MVP en producción con 10 peluquerías de prueba
Duración
6 semanas, del 6 de octubre al 15 de noviembre
Ritmo
Una tarea = una rama = un PR
Equipo
Una persona y un agente

02Fases

  1. 1CimientosSemana 1
  2. 2NegocioSemana 2
  3. 3ReservaSemana 3
  4. 4PagosSemana 4
  5. 5AvisosSemana 5
  6. 6LanzarSemana 6

03Fase 1 · Cimientos

  • 1.1 Proyecto, repositorio y Vercel
  • 1.2 Supabase local y en la nube
  • 1.3 Tablas base y RLS
  • 1.4 CI con lint y tests
  • Hecho cuando: un push despliega una preview

04Fase 2 · Panel del negocio

  • 2.1 Registro y login
  • 2.2 Crear servicios
  • 2.3 Horario y descansos
  • 2.4 Agenda del día
  • Hecho cuando: una dueña configura su negocio sola

05Fase 3 · Reserva del cliente

  • 3.1 Página pública del negocio
  • 3.2 Cálculo de huecos libres
  • 3.3 Selector de hueco
  • 3.4 Retener el hueco 10 minutos
  • Hecho cuando: dos personas no pueden coger el mismo hueco

06Fase 4 · Pagos

  • 4.1 Conectar Stripe al negocio
  • 4.2 Checkout de la señal
  • 4.3 Webhook que confirma
  • 4.4 Reembolso al cancelar a tiempo
  • Hecho cuando: el pago de prueba confirma la cita

07Fase 5 · Avisos

  • 5.1 Dominio verificado en Resend
  • 5.2 Correo de confirmación
  • 5.3 Recordatorio 24 h antes con cron
  • 5.4 Cambiar y cancelar desde el enlace
  • Hecho cuando: llega el recordatorio a la hora

08Fase 6 · Lanzamiento

  • 6.1 Landing y precios
  • 6.2 Analítica del embudo
  • 6.3 Revisión de seguridad
  • 6.4 10 negocios invitados
  • Hecho cuando: entra la primera reserva real

09Lo que bloquea a qué

  • Sin 1.3 (RLS) no empieza nada que toque datos
  • 3.4 va antes que 4.2: no se cobra un hueco que no está retenido
  • 5.1 tarda hasta 48 h en verificarse: se pide en la semana 1

10Cómo se le pide a la IA

Lee AGENTS.md y PROGRESS.md.
Implementa solo la tarea 3.4 del plan.
Antes de escribir código, dime qué archivos
vas a tocar y cómo lo vas a probar.

El error es pedir «hazme la app» en un solo mensaje. Cada fase tiene que poder probarse sola; si no, cuando algo falla no sabes en cuál de las cuarenta cosas nuevas está.

11. TESTING.md: cómo sabes que funciona

Qué tipo de test para qué, los flujos que se prueban de punta a punta, los casos límite y cuándo se ejecuta cada cosa. Convierte «parece que funciona» en algo que se puede comprobar.

turnio
TESTING.mdPreview
docs › TESTING.md
TESTING.mdFunciona de verdad

Cómo se prueba

Qué se prueba, con qué, cuándo, y qué significa que algo funciona. Sin esto, «funciona» quiere decir «lo miré una vez».

01Qué tipo de test para qué

TipoHerramientaQué cubreCuántos
UnitarioVitestLógica pura: huecos, precios, fechasMuchos
IntegraciónVitest y Supabase localEndpoints, RLS y consultasUno por endpoint
De punta a puntaPlaywrightLos flujos que dan dineroPocos: 6
ManualUn móvil de verdadLo que se ve y se tocaAntes de cada versión

02Herramientas

  • VitestUnitarios e integración
  • PlaywrightNavegador real
  • Testing LibraryComponentes
  • Supabase localBase limpia en cada test
  • axeAccesibilidad
  • GitHub ActionsEn cada PR

03Flujos de punta a punta

  • Reservar y pagar la señal
  • Cancelar a tiempo y recibir el reembolso
  • Dos personas a la vez en el mismo hueco
  • La dueña crea un servicio y aparece en su página
  • Entrar con enlace mágico
  • Cambiar la cita desde el correo

04Casos límite que siempre se prueban

  • Cambio de hora de octubre y de marzo
  • Una cita que acaba a medianoche
  • El último hueco del día
  • Doble clic en «Confirmar»
  • Nombre con tildes, emojis o 80 letras
  • Conexión lenta (3G)

05Casos de prueba

IDCasoResultado esperadoTipo
T-01Pedir huecos de un día cerradoLista vacía, no errorUnitario
T-02Reservar un hueco ya retenido409 SLOT_TAKENIntegración
T-03Negocio A pide reservas de BLista vacía por RLSIntegración
T-04Webhook de Stripe repetidoUna sola confirmaciónIntegración
T-05Pago rechazadoHueco retenido y avisoPunta a punta
T-06Cancelar con menos de 24 hSe avisa y no se reembolsaPunta a punta

06Reglas

  • Cada bug arreglado deja un test que lo reproduce
  • Un test no se borra ni se salta para que pase
  • Stripe y Resend, simulados; nunca la red de verdad
  • Ningún test depende del orden de los demás
  • La zona horaria del CI se fija a Europe/Madrid

07Cuándo se ejecutan

MomentoQué
Antes de cada commitLint y unitarios de lo cambiado
En cada PRTodo, más punta a punta
Antes de publicarPasada manual en móvil
Después de publicarReserva de humo en producción

08Comandos

npm run test               # unitarios e integración
npm run test -- --watch    # mientras programas
npm run test:e2e           # Playwright
npm run test:e2e -- --ui   # viéndolo en el navegador

Nombra los casos límite de tu producto. En una app de reservas son el cambio de hora y dos personas cogiendo el mismo hueco, y la IA no los va a probar si no se los dices.

12. DEPLOYMENT.md: cómo llega a producción

No está en la mayoría de listas y es de los que más sustos ahorra: qué hay en cada entorno, qué variables hacen falta, en qué orden se sube cada cosa y cómo se deshace.

turnio
DEPLOYMENT.mdPreview
docs › DEPLOYMENT.md
DEPLOYMENT.mdSubir sin miedo

Despliegue

Cómo llega el código a producción, qué hay en cada entorno y cómo se da marcha atrás en dos minutos cuando algo sale mal.

01Entornos

EntornoDóndeCuándo se actualizaBase de datos
Locallocalhost:3000Al guardarSupabase local con datos de prueba
Previewturnio-*.vercel.appEn cada PRProyecto staging
Producciónturnio.appAl fusionar en mainProyecto de producción

02Del commit a producción

  1. 1PRRama desde main
  2. 2CILint y tests
  3. 3PreviewSe prueba en su URL
  4. 4MigracionesAntes que el código
  5. 5ProducciónFusionar en main
  6. 6HumoUna reserva de prueba

03Variables de entorno

Los nombres viven en .env.example; los valores, solo en Vercel.

VariablePara quéPública
NEXT_PUBLIC_SUPABASE_URLConectar con SupabaseSí
NEXT_PUBLIC_SUPABASE_ANON_KEYClave pública (la protege RLS)Sí
SUPABASE_SERVICE_ROLE_KEYWebhooks y cronNo
STRIPE_SECRET_KEYCrear pagosNo
STRIPE_WEBHOOK_SECRETVerificar webhooksNo
RESEND_API_KEYMandar correosNo
CRON_SECRETQue nadie más lance el cronNo
SENTRY_DSNEnviar erroresSí

04Antes de fusionar

  • CI en verde
  • Probado en la preview, también en móvil
  • Variables nuevas creadas en producción
  • Migraciones compatibles con el código de antes
  • Nada a producción un viernes por la tarde

05Migraciones en producción

  • Se aplican desde CI, nunca a mano desde tu ordenador
  • Siempre antes del código que las necesita
  • Copia de seguridad justo antes de una destructiva

06Marcha atrás

  • Código: Instant Rollback en Vercel, un clic
  • Funcionalidad nueva: se apaga su flag en PostHog
  • Base de datos: migración nueva que deshace; nunca borrar la vieja
  • Apuntarlo en PROGRESS.md con la causa

07Vigilancia

QuéCon quéAvisa cuando
ErroresSentryUn error nuevo o más de 10 en 5 minutos
CaídasMonitor externoLa web no responde 2 minutos
PagosStripeUn webhook falla
RecordatoriosVercel CronEl cron no termina
VelocidadSpeed InsightsLCP por encima de 2,5 s

08Dominio y correo

DNS
Cloudflare, con el dominio apuntando a Vercel
Correo
SPF, DKIM y DMARC verificados para Resend
HTTPS
Automático en Vercel; HSTS activado

El plan de marcha atrás se escribe antes de necesitarlo: a las once de la noche y con la app caída, nadie lo improvisa bien. Las trece comprobaciones antes de subir a producción lo completan.

13. DECISIONS.md: por qué es así

Un registro de decisiones, cada una con su contexto, lo que se descartó y lo que cuesta. Impide que una IA sin memoria deshaga en cinco minutos algo que te costó una semana decidir.

turnio
DECISIONS.mdPreview
docs › DECISIONS.md
DECISIONS.mdEl porqué de todo

Registro de decisiones

Por qué el proyecto es como es. Cada decisión con su contexto, lo que se descartó y lo que cuesta, para que nadie la deshaga sin saberlo. Tampoco la IA.

01Índice

ADRDecisiónEstadoFecha
001Next.js sin servidor aparteAceptada02/10
002Supabase en vez de FirebaseAceptada02/10
003Recordatorios por SMSSustituida por 00603/10
004El cliente reserva sin cuentaAceptada06/10
005La señal la confirma el webhook, no la páginaAceptada27/10
006Recordatorios por email; WhatsApp en la v2Aceptada28/10

02Plantilla

## ADR-000: Título corto
Fecha · Estado: propuesta | aceptada | sustituida
Contexto: qué problema había
Decisión: qué se eligió
Alternativas: qué se descartó y por qué
Consecuencias: qué ganamos y qué pagamos

03Cuándo se escribe una

  • Hay dos opciones razonables y se elige una
  • Entra una dependencia o un servicio de pago
  • El cambio cuesta mucho de deshacer
  • Alguien preguntará «¿por qué así?»

04ADR-004 · El cliente reserva sin cuenta

Contexto
En las pruebas, 4 de cada 10 clientes abandonaban al ver «Crea tu cuenta».
Decisión
Se reserva con nombre, móvil y email. La cita se gestiona con un enlace firmado.
Alternativas
Cuenta obligatoria (más abandono) o login con Google (no todas lo tienen).
Consecuencias
Más reservas. A cambio, un cliente puede aparecer repetido: se une por teléfono.
Revisar si
Añadimos fidelización o historial para el cliente.

05ADR-005 · La señal la confirma el webhook

Contexto
Si el cliente cierra la pestaña tras pagar, la página de éxito no llega a cargarse.
Decisión
Solo checkout.session.completed marca la reserva como confirmada.
Alternativas
Confirmar al volver de Stripe: rápido, pero pierde pagos.
Consecuencias
Unos segundos de «confirmando…» a cambio de no perder ni una reserva pagada.

06Reglas

  • Una decisión no se borra: se sustituye por otra
  • Cabe en una pantalla
  • La IA la redacta; tú la apruebas
  • Si la IA propone deshacer una, primero la cita

No escribas una por cada cosa. Solo cuando había dos opciones razonables o cuando deshacerlo sale caro.

14. PROGRESS.md: dónde lo dejamos

La memoria entre sesiones. La IA no recuerda la conversación de ayer, pero puede leer un archivo: qué está hecho, qué falta, en qué rama estás y qué ha fallado ya.

turnio
PROGRESS.mdPreview
docs › PROGRESS.md
PROGRESS.mdMemoria entre sesiones

Progreso

La memoria del proyecto entre sesiones: dónde lo dejamos, qué falta y qué ha salido mal. La IA lo lee al empezar y lo escribe al terminar.

01Estado actual

Fase
3 · Reserva del cliente
Última sesión
29 de octubre de 2026
Rama
feat/hold-slot
Siguiente tarea
3.4 Retener el hueco 10 minutos mientras se paga
Bloqueos
Ninguno

02Hecho

  • Fases 1 y 2 completas
  • 3.1 Página pública del negocio
  • 3.2 Cálculo de huecos, con 14 tests
  • 3.3 Selector de hueco en móvil

03Pendiente

  • 3.4 Retener el hueco (empezada)
  • Revisar textos de error con Marta
  • Probar el selector en un Android antiguo
  • Deuda: el selector de hueco carga dos veces

04Trampas conocidas

Lo que ya ha fallado una vez y no tiene que volver a fallar.

ProblemaCausaSolución
Huecos una hora corridosSe calculaban en la hora del servidorTodo en UTC y se convierte al pintar
La agenda salía vacía sin errorFaltaba la política select de RLSTest de RLS por tabla
Una reserva confirmada dos vecesStripe reintenta los webhooksGuardar event.id
E2E en verde en local y en rojo en CIEl CI corre en UTCTZ=Europe/Madrid en el CI

05Últimas sesiones

FechaQué se hizoCommit
29/10Selector de hueco y sus estados vacío y errora41c9e2
28/10Cálculo de huecos con descansos y festivos7be0d13
27/10Página pública y ADR-005f93a6c1

06Antes de cerrar la sesión

  • Actualiza el estado actual y la siguiente tarea
  • Mueve lo terminado a «Hecho»
  • Si algo falló, apunta la trampa y su solución
  • Si se decidió algo, escribe su ADR
  • Commit: docs(progress): cierre de la sesión

Lo más valioso son las trampas conocidas: cada error apuntado es un error que el agente no repite. Ciérralo al final de cada sesión con el segundo prompt del artículo.

Que los lea y que sigan siendo verdad

Un documento desactualizado es peor que ninguno, porque la IA se lo cree. Seis reglas para que no pase:

  • Cada dato en un solo sitio. Si el precio de la señal está en el PRD y en la base de datos, un día dirán cosas distintas. Uno lo cuenta y el otro enlaza.
  • En el mismo commit. El cambio de código que contradice un documento lo corrige en el mismo commit, no «luego».
  • Cortos. Una o dos pantallas cada uno. Lo que no ayuda al agente a decidir, sobra.
  • Nómbralos en el prompt. «Sigue DATABASE.md» funciona mejor que esperar a que lo encuentre.
  • Cifras en vez de adjetivos. «Menos de 2,5 segundos» se puede comprobar; «rápido», no.
  • Revísalos cada mes. Pide al agente que compare cada documento con el código y te diga dónde mienten.

Los tres prompts: el primero te hace las preguntas y escribe los documentos, el segundo cierra cada sesión y el tercero comprueba que la documentación sigue diciendo la verdad.