Portada de 🚀 Checklist Pre-Deploy para Vibecoders
Tutorial Acceso Libre 15 May, 2026

🚀 Checklist Pre-Deploy para Vibecoders

12 puntos que separan un side-project de algo en producción. No es exhaustivo. Es lo mínimo no-negociable. Si vibecodeas con Cursor, Claude o ChatGPT, deployas en Vercel, Netlify o Railway, y usas Supabase, Clerk o Stripe — esta guía está hecha para ti. La idea es simple: antes de pasar a producción, recorrer estos 12 puntos uno por uno. Te toma 30 minutos la primera vez. Después, 10.

Contenido

1. 🔑 Secrets en .env, nunca en el repo

Qué es: Las API keys de OpenAI, Anthropic, Stripe, Supabase, Resend — todo lo que tenga una cadena tipo sk-..., pk_..., eyJ... — va en un archivo .env local y en las variables de entorno de tu plataforma. Nunca en el código.

Por qué importa: Si pusheas una API key al repo público, hay bots que escanean GitHub cada minuto. Tu key dura entre 30 segundos y 5 minutos antes de que alguien la use para minar cripto o quemarte la cuota.

Cómo se ve hecho:

  • .env está en .gitignore
  • Hay un .env.example con los nombres de las variables, sin valores
  • Si ya pusheaste una key: rótala en el dashboard del proveedor. Borrar el commit no la limpia.

Verifica: corre git log -p | grep -iE "sk-|api_key|secret" en tu repo. Si aparece algo, hay key expuesta en el historial.


2. ⚙️ Env vars cargadas en producción

Qué es: Las mismas variables que tienes en .env local tienen que estar pegadas en el dashboard de Vercel, Railway, Netlify o donde sea que deployes.

Por qué importa: El error más común post-deploy: "funciona en local pero en producción truena". 8 de cada 10 veces son env vars que olvidaste configurar en el dashboard, o que copiaste con un espacio extra.

Cómo se ve hecho:

  • Cada variable de .env.example está creada en producción
  • Las variables sensibles están marcadas como "secret" en la plataforma (Vercel lo hace por defecto)
  • Si tienes ambientes preview/staging, las env vars también están ahí — no solo en production
  • En Next.js, las que se usan en el cliente empiezan con NEXT_PUBLIC_

Verifica: abre la consola del navegador en producción y revisa que no haya errores tipo undefined is not a function o Cannot read property X of undefined por env vars faltantes.


3. 💸 Spending limits en APIs de IA

Qué es: Un límite máximo de gasto mensual en OpenAI, Anthropic, Replicate, ElevenLabs, o cualquier API que cobre por uso.

Por qué importa: Un bug en un loop, un usuario malicioso, o un agente que se enreda en sí mismo, te pueden generar $3,000 en una madrugada. Sin límite, la tarjeta se cobra. Con límite, la API responde con error y tú duermes.

Cómo se ve hecho:

  • En OpenAI: Settings → Limits → "Monthly budget" + "Email alert threshold"
  • En Anthropic Console: Settings → Plans & billing → spend limits
  • En Stripe (si cobras): tienes alertas de gastos inusuales activadas
  • Las alertas llegan a un email que sí revisas, no a uno que ignoras

Verifica: revisa que tengas un límite numérico, no solo "alertas". El límite tiene que cortar el servicio, no solo avisarte.


4. 🔒 Auth + autorización en cada endpoint

Qué es: Dos cosas distintas:

  • Autenticación: el endpoint sabe quién está llamando (token, sesión)
  • Autorización: el endpoint sabe si esa persona puede hacer esa acción

Por qué importa: El frontend esconde el botón de "borrar usuario", pero el endpoint DELETE /api/users/123 está abierto a cualquiera que lo descubra. Otro clásico: el user_id viene del frontend en lugar de leerse del token de sesión, así que cualquiera edita los datos de cualquiera cambiando un número en la URL.

Cómo se ve hecho:

  • Cada endpoint del backend tiene middleware de auth, no solo las vistas del frontend
  • El user_id se lee siempre del token/sesión del servidor, nunca del request body o de la URL
  • Cada query a la base de datos filtra por el user_id autenticado: where user_id = session.user.id
  • Si usas Supabase: Row Level Security (RLS) está activado en cada tabla
  • Hay roles separados (admin, user) y se validan en el backend

Verifica: abre dos cuentas de prueba. Logueado como usuario A, intenta acceder a un recurso de usuario B cambiando el ID en la URL o en el request. Si funciona, hay agujero.


5. 🛡️ Inputs validados + queries parametrizadas

Qué es: Toda data que viene del usuario (formularios, URL params, headers) se valida con un schema antes de tocar la lógica. Y toda query a la DB usa parámetros, no concatenación de strings.

Por qué importa: Sin validación, un email puede ser 10KB de texto malicioso. Sin queries parametrizadas, alguien escribe '; DROP TABLE users;-- y te borra la base de datos. SQL injection sigue siendo el top 1 de OWASP por algo.

Cómo se ve hecho:

  • Validación con Zod, Yup, o Valibot en cada endpoint (no solo en el frontend)
  • ORMs como Prisma, Drizzle, o el SDK de Supabase — todos parametrizan por defecto. Si escribes SQL crudo, usa placeholders ($1, ?), nunca template strings con datos del usuario
  • Outputs del usuario que se muestran a otros usuarios pasan por escape de HTML (React lo hace por defecto, pero dangerouslySetInnerHTML lo rompe)
  • Límite de tamaño en cada campo (longitud máxima de strings, tamaño máximo de uploads)

Verifica: intenta enviar un campo name con 10,000 caracteres, un email sin @, un número negativo donde debería ser positivo. ¿La API responde con error 400, o lo acepta?


6. ⏳ Reset links caducables (15-60 min)

Qué es: Los enlaces de "olvidé mi contraseña", verificación de email, o invitaciones, expiran después de un tiempo corto.

Por qué importa: Un link de reset que dura para siempre es una llave maestra. Si el correo del usuario se compromete meses después, ese link sigue siendo válido.

Cómo se ve hecho:

  • Reset de contraseña: 15-60 minutos
  • Verificación de email: 24 horas máximo
  • Invitaciones a equipo: 7 días, o un solo uso
  • El token es de un solo uso: después de usarlo, se invalida
  • Si usas Supabase Auth, Clerk, o Auth0, esto viene configurado — verifica el valor por defecto en el dashboard

Verifica: pide un reset, espera el tiempo de expiración + 5 minutos, intenta usar el link. Debe responder "link expirado", no permitir el reset.


7. 🚦 Rate limiting por IP y por usuario

Qué es: Un máximo de requests por minuto/hora por IP, por usuario, y por endpoint sensible.

Por qué importa: Sin rate limiting, alguien puede:

  • Probar 10,000 contraseñas por segundo en tu login (brute force)
  • Pedir 5,000 resets de contraseña por minuto y saturar tu cuota de Resend/SendGrid
  • Llamar tu endpoint que usa OpenAI 50 veces por segundo y quemarte la cuenta

Cómo se ve hecho:

  • Login, signup, reset password, forgot password: 5-10 intentos por hora por IP
  • Endpoints que usan APIs pagas: 10-30 por minuto por usuario
  • Endpoints públicos: 60-100 por minuto por IP
  • Herramientas: Upstash Rate Limit (gratis para empezar), Vercel Rate Limit, o middleware propio con Redis

Verifica: abre una terminal y corre for i in {1..20}; do curl https://tu-app.com/api/login; done. Después del intento 5-10 debes ver respuestas 429 "Too Many Requests".


8. 🪝 Webhooks verificados con firma

Qué es: Cuando Stripe, Clerk, GitHub o cualquier servicio te manda un webhook, llega con un header de firma. Tu endpoint debe verificar esa firma antes de procesar el evento.

Por qué importa: Tu endpoint de webhook es público — cualquiera puede llamarlo. Sin verificación de firma, alguien puede mandarte un POST falso simulando "Stripe dice que este usuario pagó $1,000" y darle acceso premium sin haber cobrado nada.

Cómo se ve hecho:

  • Stripe: usar stripe.webhooks.constructEvent() con el STRIPE_WEBHOOK_SECRET
  • Clerk: usar @clerk/nextjs con el WEBHOOK_SECRET
  • GitHub: verificar el header x-hub-signature-256 con HMAC
  • Nunca confiar en el body del webhook sin verificar la firma primero
  • El webhook secret está en env vars, igual que el resto

Verifica: intenta llamar tu endpoint de webhook con curl y un body falso. Debe responder con error 400 o 401, no procesar el evento.


9. 📊 Logs + Sentry + alertas activas

Qué es: Tres capas distintas:

  • Logs: registros de qué pasó (consultables después)
  • Error tracking (Sentry): te avisa cuándo algo falla, con stack trace y contexto
  • Alertas: notificación activa (email, Slack, SMS) cuando algo crítico se rompe

Por qué importa: Sin esto, te enteras de los bugs por el tweet enojado del usuario. Con esto, te enteras en 60 segundos y arreglas antes de que se note.

Cómo se ve hecho:

  • Sentry configurado en frontend y backend (free tier alcanza para empezar)
  • Logs estructurados (JSON) en lugar de console.log sueltos
  • Alertas configuradas para: error rate > X%, latencia > Y ms, downtime, webhooks que fallan
  • Los errores no exponen información sensible en los logs (no loguees passwords, tokens, números de tarjeta)

Verifica: fuerza un error en producción (un endpoint con un throw new Error()). ¿Te llegó la alerta? ¿En cuánto tiempo? Si no llega, no está configurado bien.


10. 💾 Backup automático de la DB confirmado

Qué es: Tu base de datos se respalda automáticamente, y tú sabes cómo restaurarla.

Por qué importa: Un DELETE FROM users sin WHERE (sí, pasa), una migración mal hecha, un servidor que se corrompe — sin backup, perdiste el negocio. Con backup, pierdes un día.

Cómo se ve hecho:

  • Supabase: Daily backups vienen en el plan Pro. En Free tier, no hay backup automático — usa pg_dump manual o paga el upgrade si tienes data real
  • Neon, Railway, PlanetScale: revisa el dashboard, confirma frecuencia (idealmente diario)
  • Sabes cómo restaurar un backup. Probaste el proceso al menos una vez en un proyecto sandbox
  • Los backups se guardan en una región distinta (o al menos, no en el mismo servidor)

Verifica: entra al dashboard de tu DB, busca "Backups". Si no ves la palabra "automated" o "daily", no estás cubierto.


11. 🧪 Flujo crítico probado en el dominio real

Qué es: Después del deploy, no asumas que funciona porque "abrió la página". Recorre el flujo completo de tu producto en el dominio de producción, como si fueras un usuario nuevo.

Por qué importa: En localhost, Stripe usa modo test, los emails se mandan a tu inbox, y los redirects van a localhost:3000. En producción, todo cambia. El bug aparece en el momento más caro: cuando un usuario real está pagando.

Cómo se ve hecho: abre una ventana de incógnito y:

  1. Signup con un email real (puedes usar un alias tipo tu+test@gmail.com)
  2. Verifica que el email de confirmación llega y el link funciona
  3. Login con la cuenta nueva
  4. Recorre el flujo principal (crear, editar, borrar lo que sea tu core)
  5. Si cobras: paga $1 (o usa Stripe en modo test con la key de prod si es posible)
  6. Verifica que el webhook se procesa y el acceso se desbloquea
  7. Logout, login otra vez, verifica que el estado persiste

Verifica: si cualquier paso falla o se siente raro, no lances. Aún si tu app "se ve bien".


12. ⏪ Rollback al deploy anterior en un clic

Qué es: Si el deploy que acabas de hacer rompe algo, puedes volver al anterior en menos de 60 segundos.

Por qué importa: Todo el mundo deploya bugs. La diferencia entre un susto y una crisis es el tiempo que pasa entre "se rompió" y "ya está arreglado". El rollback te da minutos en lugar de horas.

Cómo se ve hecho:

  • Vercel/Netlify: cada deploy queda guardado. Vas a Deployments → click en uno anterior → "Promote to Production"
  • Railway/Render: los deploys se hacen por commit, así que git revert + push hace rollback
  • Las migraciones de base de datos son reversibles, o tienes un plan para reversarlas a mano
  • Sabes dónde está el botón. Lo encontraste antes de necesitarlo

Verifica: abre el dashboard de tu plataforma ahora mismo, busca el deploy anterior, y confirma que sabes cómo promoverlo. Si te toma más de 30 segundos encontrar dónde, no es un "rollback en un clic".


Plantilla de .env.example

Pega esto en la raíz de tu proyecto y adapta a lo que uses:

# App
NEXT_PUBLIC_APP_URL=https://tu-app.com

# Database (Supabase / Neon / etc.)
DATABASE_URL=
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
SUPABASE_SERVICE_ROLE_KEY=

# Auth (Clerk / Auth0 / etc.)
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=
CLERK_SECRET_KEY=
CLERK_WEBHOOK_SECRET=

# AI
OPENAI_API_KEY=
ANTHROPIC_API_KEY=

# Pagos
STRIPE_SECRET_KEY=
STRIPE_WEBHOOK_SECRET=
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=

# Email
RESEND_API_KEY=

# Observability
SENTRY_DSN=
NEXT_PUBLIC_SENTRY_DSN=

Cómo usar este checklist

Primera vez: imprímelo o pégalo en un Notion. Recorre los 12 puntos uno por uno. Te va a tomar entre 1 y 3 horas la primera vez según el estado de tu proyecto.

Cada deploy nuevo: revisión rápida de los puntos que pueden cambiar (env vars, rollback, smoke test). Toma 10 minutos.

Cuando lances algo nuevo grande: los 12 otra vez. Sin atajos.


La regla de oro

Si no puedes responder "sí, está hecho" a los 12, no es momento de deploy. Es momento de cerrar el laptop y dormir. Vuelve mañana, termina los pendientes, y entonces sí.

El deploy que se rompe a las 2 AM cuesta más que el lanzamiento que se atrasa 24 horas. Siempre.


Hecho por Erik Taveras · Taveras Solutions LLC

Únete a la Comunidad

Regístrate gratis para descargar archivos, guardar recursos en favoritos, ganar XP y acceder a cursos y el foro de la comunidad.

¿Ya tienes cuenta? Inicia sesión

Erik Taveras

Autor

Erik Taveras

Recursos Relacionados

Tutorial
Portada de Vibecoder Dictionary 2026

Vibecoder Dictionary 2026

Las primeras 25 las viste en el reel. Las otras 25 son las que tarde o temprano te van a aparecer en Cursor, en Claude Code, en el deploy, en el error que no entiendes a las 2am.

142 0
Tutorial
Portada de Readdy AI sin quemar créditos

Readdy AI sin quemar créditos

Guía práctica — Erik Taveras Cómo estructurar tu prompt para que genere bien a la primera, más el checklist de diseño que uso para entregar sitios a clientes.

199 0
Tutorial
Portada de Guía — Cómo aprovechar MaxHermes (Hermes Agent en la nube)

Guía — Cómo aprovechar MaxHermes (Hermes Agent en la nube)

La mayoría de asistentes de IA olvidan todo entre conversaciones. Cada vez que abres un chat nuevo, vuelves a explicar quién eres, qué estás trabajando, cómo te gusta que organicen la información, qué formato usas para tus reportes. Ese costo no aparece en la factura mensual, pero se siente todos los días.

207 0