Checklist de despliegue
Esta página es la que se recorre cada vez que un cambio va a producción.
Es la compañera de Desplegar, paso a paso, y la división es esta:
| Página | Contesta |
|---|---|
| Desplegar, paso a paso | Cómo se despliega: los tres casos de migración, la cadena de Supabase, el rollback |
| Esta | Qué verifico en cada ambiente, en qué orden, y cómo protejo los datos |
Lo que cambió, y por qué esta página existe
Sección titulada «Lo que cambió, y por qué esta página existe»Producción dejó de ser un ambiente de prueba. Tiene clientes reales: nombres, teléfonos y direcciones de gente de Campo de la Cruz que confió esos datos en el mostrador.
Eso cambia el precio de un error. Antes, romper producción costaba una tarde. Ahora puede costar información que no se puede volver a pedir — nadie va a recorrer el pueblo preguntando otra vez.
El recorrido, de un vistazo
Sección titulada «El recorrido, de un vistazo»Cinco fases. Cada una tiene una salida verificable: si no la ves, no seguís.
| Fase | Dónde | Prueba de que pasó | |
|---|---|---|---|
| 0 | Tu máquina | local | Todo lo que corre CI, en verde |
| 1 | El preview | Vercel + Railway staging |
El flujo nuevo, hecho a mano |
| 2 | El respaldo | Supabase → tu disco | ✓ respaldo verificado ... N tablas |
| 3 | El despliegue | GitHub → Railway + Vercel | El caso A, B o C |
| 4 | La comprobación | producción | Una operación real, hecha por vos |
Fase 0 — Tu máquina
Sección titulada «Fase 0 — Tu máquina»Correr localmente todo lo que corre CI. No los tests solamente: todo. Un contrato de endpoint que cambia rompe la colección de Bruno aunque los tests unitarios estén verdes.
0.1 · Las migraciones, en las DOS bases
Sección titulada «0.1 · Las migraciones, en las DOS bases»cd ~/code/aquazaku/api && pnpm db:migratecd ~/code/aquazaku/api && pnpm db:migrate:testSon dos comandos porque son dos bases. Olvidarse del segundo cuesta media hora buscando un bug que no existe: los tests fallan por columnas inexistentes y parece del código.
El migrador anuncia el destino antes de tocar nada:
→ migrando aquazaku@localhost/aquazaku_test → schema "public"✓ migraciones aplicadas0.2 · Lo que corre api-ci
Sección titulada «0.2 · Lo que corre api-ci»cd ~/code/aquazaku/api && pnpm typecheckcd ~/code/aquazaku/api && pnpm test:coverageEsos son exactamente los dos pasos del workflow, más las migraciones de
arriba. Medido hoy, api pasa con holgura:
| Real | Umbral | |
|---|---|---|
| Statements | 91.25% | — |
| Branches | 81.45% | 65% |
| Functions | 94.60% | 70% |
| Lines | 93.12% | 70% |
0.3 · Lo que corre web-ci
Sección titulada «0.3 · Lo que corre web-ci»cd ~/code/aquazaku/web && pnpm typecheckcd ~/code/aquazaku/web && pnpm lintcd ~/code/aquazaku/web && pnpm test:coverageEl lint de web sí es ESLint de verdad, y ahí vive la regla que hace
cumplir el patrón BFF: que el browser nunca le hable directo a api/
(ADR-0002). No te lo saltes.
0.4 · Bruno, si tocaste un contrato
Sección titulada «0.4 · Bruno, si tocaste un contrato»Si cambiaste la forma de una petición o de una respuesta, la colección se rompe
aunque todo lo demás esté verde. api-ci la corre en cada push; correla antes
vos.
Necesita dos condiciones que la base de desarrollo no cumple —estar vacía y
tener un solo administrador—, así que va contra la base de tests, en el
3002. Está la configuración api-pruebas en .claude/launch.json.
Fase 1 — El preview
Sección titulada «Fase 1 — El preview»Un push a una PR despliega dos cosas más:
| Plataforma | Qué |
|---|---|
| Vercel | La URL xxx-git-<rama>-aquazaku.vercel.app |
Railway staging |
La api del preview, contra el schema preview |
Acá es donde se usa el cambio con las manos. No es opcional y no lo reemplaza ningún test: la suite verde nunca dijo nada sobre lo que se ve ni sobre lo que se siente al usarlo.
Qué mirar:
- El flujo nuevo, de punta a punta, como lo haría quien atiende.
- El flujo viejo que está más cerca del cambio. Ahí viven las regresiones.
- La consola del navegador. Un error rojo que nadie mira es un bug que se despliega.
Fase 2 — El respaldo
Sección titulada «Fase 2 — El respaldo»Antes de cualquier migración contra producción. Sin excepciones, y menos ahora que hay clientes cargados.
cd ~/code/aquazaku/apiDATABASE_MIGRATION_URL='<cadena de Supabase>' pnpm db:respaldoLo que tiene que imprimir:
→ respaldando postgres.<ref>@aws-0-us-east-1.pooler.supabase.com/postgres✓ respaldo verificado de ... — 29 tablas · N MB respaldos/aquazaku-<etiqueta>-<sello>.sql.gzTres cosas de esa salida:
- La primera línea es el destino. Leela. Si dice otra cosa que Supabase, el respaldo no sirve para lo que lo pediste.
- «verificado» no es adorno: el script compara el volcado contra la lista
real de tablas del schema. Un
pg_dumpque devuelve un archivo vacío también termina en0, y esa verificación es lo que lo atrapa. - El archivo queda en
api/respaldos/, que está en.gitignore. Ahí adentro hay datos de personas reales: nunca lo subas a un repo, ni lo pases por chat, ni lo adjuntes a un ticket.
Cómo se restaura, si hiciera falta
Sección titulada «Cómo se restaura, si hiciera falta»El propio script lo imprime al terminar:
gunzip -c respaldos/<archivo>.sql.gz | psql "$DATABASE_MIGRATION_URL"Sobre una base vacía. Restaurar encima de una base con datos no es restaurar: es mezclar.
Fase 3 — El despliegue
Sección titulada «Fase 3 — El despliegue»Acá se cruza a Desplegar, paso a paso, que tiene los tres casos con sus comandos. El resumen para decidir cuál:
| ¿Trae migración? | ¿Qué hace? | Orden |
|---|---|---|
| No | — | Mergear. Listo |
| Sí | Agrega algo que el código nuevo todavía no usa | Mergear → migrar |
| Sí | Agrega algo que el código nuevo necesita para funcionar | Migrar → mergear |
| Sí | Renombra o convierte | Migrar → mergear |
| Sí | Rompe el código viejo — una columna NOT NULL sin default, un CHECK nuevo |
Migrar → mergear, y lo más pegado posible |
El orden de los tres últimos está invertido a propósito, y no es preferencia: el otro orden tumba la planta.
Cuando la respuesta es que no, api igual se puede mergear antes de migrar
—nadie llama todavía al endpoint nuevo— pero web no, porque es quien lo
llama. El orden que sale de ahí:
- Mergear
api - Respaldo y migración
- Mergear
web
Las migraciones no corren solas. El startCommand de producción está vacío
a propósito (ADR-0011): el
container solo levanta el servidor. Migrar es un acto separado y deliberado que
hacés vos.
Fase 4 — La comprobación en producción
Sección titulada «Fase 4 — La comprobación en producción»4.1 · La consola
Sección titulada «4.1 · La consola»curl -s https://api.aquazaku.com/health{"status":"ok","service":"aquazaku-api","base":"ok","desdeHaceMs":19608}Si aparece "esquema":"pendientes" con una lista en faltan, el código
desplegado le está pidiendo a la base algo que no tiene.
4.2 · La comprobación que ninguna consola reemplaza
Sección titulada «4.2 · La comprobación que ninguna consola reemplaza»Entrá a app.aquazaku.com y hacé una operación real: registrá un cliente, cargá una venta, lo que el cambio haya tocado.
Casi todos los defectos serios de este proyecto aparecieron al usar el
sistema, no al leerlo ni al testearlo. /health en verde solo dice que el
servidor está vivo.
Lo que nunca se hace contra producción
Sección titulada «Lo que nunca se hace contra producción»Cinco reglas. Cada una está acá porque el costo de romperla no se puede deshacer.
1 · Nunca pnpm db:seed contra producción
Sección titulada «1 · Nunca pnpm db:seed contra producción»El seed crea un usuario con acceso total y siembra datos de demostración. Tiene
dos frenos —AQUAZAKU_ENV y SEED_CONFIRM=yes— justamente porque correrlo
contra la planta sería un desastre silencioso.
Si alguna vez tenés que pasar SEED_CONFIRM=yes, parate y preguntate qué estás
haciendo.
2 · Nunca cambies AQUAZAKU_ENV en el servicio de producción
Sección titulada «2 · Nunca cambies AQUAZAKU_ENV en el servicio de producción»Esa variable decide qué schema lee la aplicación:
| Valor | Schema | Servicio |
|---|---|---|
production |
public |
producción |
preview |
preview |
staging |
Si producción quedara en preview, la app leería un schema sin usuarios y sin
clientes. /health seguiría diciendo ok —preview tiene las mismas tablas—
y el login diría «Credenciales inválidas» con la contraseña correcta.
Se ve como un problema de cuentas y es un problema de configuración.
3 · Nunca migres sin el respaldo de la Fase 2
Sección titulada «3 · Nunca migres sin el respaldo de la Fase 2»Revertir el código no deshace una migración. Si ya corrió, hay que escribir otra que la deshaga explícitamente. Por eso el respaldo no es ceremonia: es la única vuelta atrás que existe para los datos.
4 · Nunca uses pnpm db:migrate:prod
Sección titulada «4 · Nunca uses pnpm db:migrate:prod»Ese atajo corre tsx --env-file-if-exists=.env.produccion.local. Si ese archivo
quedó apuntando a otro lado —pasa— migrás la base equivocada y el comando te
dice «✓ migraciones aplicadas» igual.
La forma explícita no depende de ningún archivo:
DATABASE_MIGRATION_URL='...' pnpm db:migrate5 · Nunca pegues la cadena de conexión en un chat, un log o una captura
Sección titulada «5 · Nunca pegues la cadena de conexión en un chat, un log o una captura»La cadena incluye la contraseña del rol postgres, que es dueño de la base:
lee todo, borra todo, y puede saltarse los REVOKE que protegen el
audit_log.
Por eso el migrador y el respaldo imprimen el destino sin la contraseña: esas líneas terminan en logs y en capturas de pantalla, y se escribieron sabiéndolo.
Cuando algo sale mal
Sección titulada «Cuando algo sale mal»| Síntoma | Qué pasó | Qué hacer |
|---|---|---|
/health dice faltan: [...] |
Se mergeó sin migrar | Correr la migración |
| 500 en todas las pantallas de clientes | Casi siempre el schema atrasado | Mirar /health |
| «Credenciales inválidas» con la clave correcta | Puede ser AQUAZAKU_ENV mal puesto |
Regla 2 |
ENOTFOUND db.<ref>.supabase.co |
Es la conexión directa, que es IPv6 | Usar la del pooler |
El migrador anuncia localhost |
La cadena no es la de producción | Ctrl+C |
| El preview no acepta ningún usuario | La api de staging no arrancó, o falta el seed | Log del deploy en Railway |
| CI en rojo pero GitHub deja mergear | main no tiene protección de rama |
Decidirlo aparte |
Un solo administrador es un punto único de falla
Sección titulada «Un solo administrador es un punto único de falla»Hoy producción tiene un usuario. Si esa cuenta se pierde —contraseña olvidada sin acceso al correo, cuenta desactivada por error— la planta queda afuera de su propio sistema.
Crear un segundo administrador es la clase de tarea que parece opcional hasta el día que no lo es.
