Previews por PR: una URL efímera por cada cambio
Revisar un cambio de frontend leyendo el diff es adivinar; revisarlo sobre una URL donde el cambio ya está desplegado es ver. Las previews por PR despliegan cada pull request a un entorno efímero con dirección propia, para revisar antes de fusionar. Descansan en el modelo de despliegues inmutables, donde cada build es un despliegue único e inmutable y producción es solo un alias que apunta a uno de ellos. Así la preview es, literalmente, el mismo artefacto que se promoverá a producción.
Revisar un cambio de interfaz leyendo el diff es una forma de adivinación: infieres cómo se verá y se comportará el resultado a partir del código, y aciertas a medias. Las previews por PR sustituyen esa inferencia por la observación directa. Cada pull request se despliega automáticamente a un entorno efímero con una URL propia, de modo que cualquiera —tú, un revisor, una diseñadora, un gestor de producto— abre el enlace y ve el cambio funcionando antes de que se fusione. Lo notable no es la comodidad, sino la fidelidad: la preview no es una aproximación al artefacto que irá a producción, es exactamente ese artefacto desplegado. Y eso es posible porque debajo hay un modelo preciso —el de los despliegues inmutables— en el que producción no es un lugar sino un puntero.
- Entender la preview por PR como el artefacto real desplegado, no una aproximación.
- Comprender el modelo de despliegues inmutables donde producción es un alias.
- Cablear el flujo: construir el PR, desplegar a un entorno efímero y publicar la URL.
- Garantizar que las previews son efímeras de verdad: se crean y se destruyen solas.
Qué es una preview y por qué cambia la revisión
Una preview por PR es un despliegue completo y aislado de la rama del pull request, accesible en una dirección única y temporal. No es un servidor de desarrollo ni una captura de pantalla: es la aplicación construida y servida como lo estaría en producción, solo que en un entorno paralelo que existe mientras el PR está abierto. Ese cambio de sustrato transforma la revisión. Un revisor deja de razonar sobre si el diff implica el comportamiento correcto y pasa a comprobarlo; una diseñadora revisa el resultado renderizado y no una promesa; y las herramientas automáticas —una auditoría de Lighthouse, una suite de Playwright— corren contra un despliegue real en lugar de contra un localhost que puede diferir del entorno final.
Revisión visual
Quien revisa abre la URL y ve el cambio funcionando. La aprobación deja de basarse en imaginar el resultado del diff.
Pruebas sobre lo real
Lighthouse y los e2e corren contra la preview desplegada, no contra un servidor local que podría no parecerse a producción.
Revisión no técnica
Producto y diseño validan con un enlace, sin clonar el repo ni levantar nada. El feedback llega antes de fusionar.
El efecto más profundo es sobre cuándo se descubren los problemas. Sin previews, un fallo visual o de comportamiento se cuela hasta que alguien lo ve en producción, o en el mejor caso hasta que un revisor levanta la rama en su máquina, cosa que casi nadie hace. Con previews, ese fallo es visible en el propio PR, con un clic, para todo el que participe en la revisión. El coste de descubrir un defecto se desploma porque se descubre antes y con menos fricción, y ese adelanto es el verdadero rendimiento de la técnica.
Despliegues inmutables: producción es un puntero
Las previews no son una función aislada, sino la consecuencia visible de un modelo de despliegue concreto. En el modelo de despliegues inmutables, cada build produce un despliegue único e inmutable: un conjunto de artefactos congelados, con su propia URL permanente, que nunca se modifica después de crearse. Producción no es un servidor que actualizas in situ; es un alias —un puntero con nombre estable, tu dominio— que apunta a uno de esos despliegues inmutables. Desplegar a producción no reescribe nada: solo reapunta el alias a un despliegue nuevo.
flowchart TD PR[abre o actualiza PR] --> B[CI construye la rama] B --> DEP[despliegue inmutable con URL unica] DEP --> U[bot publica la URL en el PR] U --> RV[revision visual y e2e sobre la preview] RV --> M[merge a main] M --> AL[alias de produccion apunta al despliegue] M --> TD[la preview se destruye] style DEP fill:#cba6f7,color:#11111b style AL fill:#a6e3a1,color:#11111b style TD fill:#f38ba8,color:#11111b
Este modelo tiene una elegancia que conviene apreciar: una preview y un despliegue de producción son el mismo tipo de objeto. Ambos son despliegues inmutables con su URL; la única diferencia es que al de producción, además, le apunta el alias del dominio. Por eso promover una preview a producción es una operación casi instantánea y sin riesgo de reconstrucción: no se compila nada nuevo, se reapunta el alias al despliegue que ya revisaste. Y por eso, como veremos en la lección de post-despliegue, revertir es igual de barato: se reapunta el alias al despliegue inmutable anterior, que sigue existiendo intacto. La inmutabilidad es lo que hace que revisar, promover y revertir sean todas variaciones de una sola operación: mover un puntero.
El punto que da a las previews su valor es la identidad, no la semejanza. En plataformas como Vercel, Netlify o Cloudflare Pages, el despliegue inmutable que revisas en la preview es exactamente el que se promoverá a producción si fusionas: mismos artefactos, mismo hash, misma configuración de build. No se reconstruye al fusionar. Esto elimina de raíz la clase de bug “se veía bien en la preview pero rompió al desplegar”, porque no hay un segundo build donde algo pueda divergir. Cuando promover es reapuntar un alias y no recompilar, la preview y producción son el mismo bit por bit.
El cableado: construir el PR y publicar la URL
El flujo, cuando lo montas tú o cuando lo hace la plataforma por ti, tiene siempre las mismas piezas. Un disparador sobre eventos de pull request; un build de esa rama; un despliegue del resultado a un entorno de preview; y la publicación de la URL de vuelta en el PR, como comentario o como deployment status de GitHub, para que sea visible sin salir de la revisión. Las plataformas de frontend lo ofrecen nativo —conectas el repositorio y cada PR obtiene su preview sin escribir CI—, pero saber cablearlo a mano desmitifica la magia.
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
preview:
runs-on: ubuntu-latest
permissions: { pull-requests: write, deployments: write }
steps:
- uses: actions/checkout@v4
- run: pnpm install --frozen-lockfile
- run: pnpm build
- id: deploy
run: echo "url=$(pnpm run deploy:preview)" >> "$GITHUB_OUTPUT"
- uses: actions/github-script@v7
with:
script: |
github.rest.issues.createComment({
...context.repo, issue_number: context.issue.number,
body: `Preview lista: ${{ steps.deploy.outputs.url }}`
})
Dos detalles no son decorativos. El primero es el disparador synchronize: sin él, la preview se crea al abrir el PR pero no se actualiza cuando empujas commits nuevos, y revisarías una versión vieja. Con él, cada empujón produce un despliegue inmutable nuevo y actualiza la URL, de modo que la preview siempre refleja el último estado de la rama. El segundo es la protección de la preview: un despliegue efímero suele ser públicamente accesible por su URL, así que si expone datos sensibles conviene protegerlo con contraseña o SSO, algo que las plataformas ofrecen como deployment protection. Una preview abierta al mundo es cómoda para revisar y peligrosa para filtrar.
El disparador pull_request desde un fork corre código que aún nadie revisó, y darle acceso a los secretos de producción para desplegar es exactamente la puerta que un atacante busca: un PR malicioso podría exfiltrar el token de despliegue. La regla es tratar las previews de forks como no confiables: constrúyelas sin secretos sensibles, o despliégalas a un entorno de preview aislado con credenciales propias y de alcance mínimo, nunca con las llaves que tocan producción. La comodidad de previsualizar el PR de un desconocido no justifica prestarle las claves de tu infraestructura.
Efímero de verdad: crear y destruir
La palabra efímero es la mitad importante del concepto y la que más se descuida. Una preview que se crea pero no se destruye deja de ser una preview y se convierte en basura acumulada: cientos de despliegues huérfanos de PRs cerrados hace meses, consumiendo recursos y ensuciando la lista. El ciclo de vida completo tiene dos extremos, y el segundo es tan importante como el primero: la preview nace al abrir el PR y muere al cerrarlo o fusionarlo. Cablear la destrucción es lo que mantiene el sistema limpio.
on:
pull_request:
types: [closed]
jobs:
teardown:
runs-on: ubuntu-latest
steps:
- run: pnpm run deploy:preview:destroy --ref "${{ github.head_ref }}"
Las plataformas gestionadas hacen esta poda por ti: al cerrar el PR, retiran su preview. Si lo montas a mano, el evento closed es el gancho donde enganchar el desmontaje. La disciplina de destruir tiene un beneficio que va más allá del orden: te obliga a que tus entornos de preview sean desechables por diseño, sin estado precioso que temas perder, y esa cualidad —poder crear y tirar un entorno completo sin ceremonia— es la misma que hace robusto todo lo demás, desde los tests de integración hasta la recuperación ante desastres. Un entorno que puedes destruir sin miedo es un entorno que entiendes por completo.
Todo el valor de las previews por PR se puede enunciar como el cierre de una brecha que durante décadas dimos por inevitable: la distancia entre revisar un cambio y comprobarlo. En el modelo antiguo, revisar era leer un diff y razonar hacia adelante sobre sus efectos —un acto de imaginación disciplinada, pero imaginación al fin—, mientras que comprobar de verdad exigía fusionar y desplegar, es decir, ocurría después de tomar la decisión que la comprobación debía informar. El orden estaba invertido: decidías y luego averiguabas. La preview reordena esa secuencia poniendo el artefacto real, desplegado y navegable, delante del revisor antes de la decisión de fusionar, y con ello convierte la revisión de un ejercicio de predicción en uno de observación. Pero fíjate en que esto no es un truco de las plataformas, sino la consecuencia de un modelo de despliegue más profundo cuya idea central vale para todo lo que viene después: la inmutabilidad. Cuando cada build es un despliegue congelado con dirección propia, y producción no es un servidor mutable sino un alias que apunta a uno de ellos, entonces revisar, promover y revertir dejan de ser operaciones distintas y peligrosas para volverse la misma operación trivial y segura: mover un puntero entre objetos que nunca cambian. La preview es solo el primer beneficio visible de esa inmutabilidad; el despliegue instantáneo y el rollback instantáneo son los otros dos, y los tres brotan de la misma raíz. Interiorizar esto cambia cómo piensas un sistema de entrega: dejas de verlo como una tubería que transforma código en un servidor vivo, y empiezas a verlo como una fábrica de artefactos inmutables sobre la que un puñado de punteros —producción, cada preview— deciden cuál es visible y dónde. En ese modelo, la pregunta “cómo se verá esto en producción” tiene una respuesta exacta y no una estimación: se verá así, porque esto es eso.
- Conecta un repositorio a una plataforma con previews nativas y abre un PR trivial; confirma que aparece una URL de preview en el PR.
- Empuja un commit nuevo a esa rama y verifica que la preview se actualiza sola gracias al evento de sincronización.
- Corre una auditoría de Lighthouse o un e2e apuntando a la URL de la preview, no a
localhost, y compara el resultado. - Cierra el PR sin fusionar y comprueba que la preview se destruye y su URL deja de responder.
- Investiga cómo tu plataforma protege las previews de forks para no filtrarles secretos, y verifica que el PR de un fork no recibe el token de producción.