Lanzar una app pequeña no significa saltarse producción: el checklist de Ultimate Porra

Ultimate Porra empezó como una app acotada: una porra para el Mundial, pocos usuarios, reglas claras y una ventana de uso muy concreta.

Ese tipo de producto puede engañar. Como no es un SaaS grande, parece que no necesita el mismo rigor operativo. Pero en cuanto hay login, emails, predicciones, resultados oficiales y un dominio público, deja de ser "una web pequeña" y pasa a ser una superficie de producción.

La pregunta que ordenó el lanzamiento fue esta:

Important

¿Qué tiene que ser verdad antes de invitar a usuarios reales, aunque la app sea pequeña?

La respuesta no fue un stack. Fue una lista corta de invariantes — propiedades que tenían que cumplirse independientemente de qué servicios hubiera detrás. Lo útil de este resumen son los invariantes, no la topología.

Separación de responsabilidades

La app puede ser pequeña; la superficie no. Incluso un producto pequeño gana manteniendo las responsabilidades separadas: una entrada pública, una capa en servidor que guarda todo lo sensible, los datos y reglas del propio producto, la identidad y el email saliente. La tecnología concreta importa menos que las fronteras entre esos trabajos — cada uno puede cambiar sin arrastrar a los demás.

Primer invariante: el navegador no toca secretos

El navegador solo debe llamar a rutas same-origin del producto. Todo lo sensible — credenciales administrativas, secretos de sesión, claves de proveedor — se queda en servidor, detrás de una capa fina con la que el navegador habla pero en la que nunca ve hacia dentro.

Esa capa no es decorativa. Es la frontera que mantiene las operaciones privilegiadas fuera del JavaScript del navegador: el cliente pide, la capa de servidor decide y guarda las credenciales. Un bundle de navegador filtrado nunca debería bastar para ejecutar una acción administrativa.

Segundo invariante: el dominio técnico no es el producto

Los proveedores de hosting estático suelen generar un dominio técnico tipo https://<proyecto>.pages.dev. Sirve para validar el deploy antes de que el dominio custom esté activo. Pero cuando el dominio custom existe, el técnico no debe quedar como segunda puerta pública — duplica el origen, debilita el dominio canónico y complica cookies, CORS, analytics y enlaces de email.

La política después del lanzamiento es redirigir el dominio técnico al canónico (<proyecto>.pages.dev/* -> 301 -> dominio custom) y mantener los preview deployments detrás de control de acceso cuando se usan para QA.

Note

Un dominio técnico puede ser útil para diagnosticar. No debe convertirse en una URL de producción paralela.

Tercer invariante: un perímetro que se puede esquivar es opcional

Poner un edge delante de una API no protege nada por sí solo si una petición puede llegar al origen por otro camino. El invariante es directo:

Warning

Si una petición puede evitar el perímetro y llegar igual al origen, el perímetro es opcional.

Así que el trabajo no acaba en "poner el edge delante". Acaba en hacer que el origen rechace cualquier cosa que no haya llegado por el camino esperado. Cómo se consigue depende de lo que soporte el runtime; la propiedad que hay que garantizar es la misma sea cual sea el mecanismo.

Ajusta la protección al riesgo de la ruta

No todas las rutas tienen el mismo riesgo, y los controles deben reflejarlo. Las lecturas públicas de bajo riesgo pueden ir ligeras. Las acciones autenticadas necesitan identidad y límites por usuario. La clase destructiva y operativa — cualquier cosa que cambie estado oficial — recibe los controles más fuertes y queda cerrada al público por defecto. La frase práctica: el edge reduce superficie; el backend decide permisos.

El email también forma parte del lanzamiento

La app no está lista si un usuario recibe un email con la marca equivocada. El mismo servicio de identidad podía crear usuarios para más de un producto, así que un email de acción tenía que llevar el contexto de producto de principio a fin — el tenant correcto, el dominio de envío correcto, la marca correcta, el destino final correcto. No es cosmético; es continuidad de confianza.

Datos iniciales: sin seed no hay producto

Para una porra del Mundial, el contenido inicial no es accesorio — la competición, la edición, los equipos y el calendario completo de partidos tienen que existir en producción antes de que nadie pueda jugar. El checklist fue concreto: ejecutar el import, verificar que están todos los partidos, confirmar fechas y fases, y luego probar crear una partida, hacer una predicción, actualizar un resultado y leer el leaderboard. Si el seed falla, la app puede estar desplegada y aun así no estar lanzable.

Operación durante el evento

Un Mundial no es un flujo continuo de software; es un calendario de eventos externos. Los resultados se pueden introducir a mano o ingerir desde un feed. Para una beta pequeña, elegir manual no es una derrota técnica — es una decisión de alcance. El punto no es automatizarlo todo; es saber qué operación manual existe, cuánto tarda y quién la hace.

La regla que me llevo

Una app pequeña no necesita una organización grande. Pero sí necesita invariantes de producción. Los míos para este lanzamiento fueron:

Bloque de codigo - text
un solo dominio público canónico
sin secretos en el navegador
sin origen esquivable
email con marca correcta
datos iniciales verificados
operación de evento escrita

Eso no convierte la app en pesada. La convierte en lanzable.

Fuentes técnicas

  • Cloudflare Pages explica cómo redirigir *.pages.dev al dominio custom usando Bulk Redirects: Redirecting *.pages.dev to a Custom Domain.
  • Cloudflare documenta cómo deshabilitar el acceso efectivo al subdominio *.pages.dev combinando Access para previews y Bulk Redirects para producción: Custom domains.
  • Cloudflare Pages permite proteger preview deployments con Access; por defecto esos previews son públicos: Preview deployments.

Este artículo forma parte de mis notas de arquitectura y operación. Si te interesa el tema, sigue el blog o escríbeme.