EN

Tu primer pipeline de DevOps: del push a producción

Leo G.7 minLeer en inglés

Cintas transportadoras de rodillos que se cruzan en un depósito automatizado.
Cintas transportadoras de rodillos que se cruzan en un depósito automatizado.

Alguien mergeó a media tarde y producción se rompió. Lo peor no es eso: es que el test que lo habría detectado ya existía. Nadie lo corrió, porque correrlo dependía de que alguien se acordara, y esa tarde nadie se acordó.

Ese es el problema que resuelve automatizar: no escribir mejores pruebas, sino sacar del medio la parte humana de acordarse. Abajo está qué conviene que corra solo cada vez que subís código, en qué orden —que importa más de lo que parece—, qué es mejor dejar afuera, y cómo eso llega a producción sin que el despliegue sea un evento. Se arma en una tarde, y la versión mínima son diez líneas.

Qué es un pipeline, en una línea

Si nunca armaste uno: un pipeline es un archivo en tu repositorio que dice qué comandos hay que correr cuando pasa algo — por ejemplo, cuando alguien sube código o abre una propuesta de cambio.

No es un programa que instalás. Es una lista de pasos que ejecuta un servidor ajeno, llamado runner, sobre una copia recién clonada de tu proyecto. Esa copia limpia es la gracia: si algo funciona en tu máquina pero falta declararlo en el repositorio, ahí se cae. El archivo se escribe en YAML, un formato de texto donde la sangría define la estructura, y vive versionado junto al código.

A la práctica de correr esos pasos en cada cambio se la llama integración continua, o CI. Cuando además el resultado se publica solo, se le suma la parte de entrega continua, o CD.

El orden importa más que el contenido

Acá está lo que casi nadie dice y es lo que más diferencia hace. La mayoría arma el pipeline poniendo los pasos en el orden en que se le ocurren. El orden correcto es del más rápido al más lento, y no es una cuestión estética.

Un pipeline que tarda dieciocho minutos en avisarte que falta un punto y coma le enseña al equipo a ignorarlo. La gente mergea sin esperar, revisa el resultado al día siguiente, y el pipeline se convierte en decoración. En cambio uno que en cuarenta segundos te dice “esto ni siquiera compila” se consulta, porque contesta rápido.

El orden que funciona, con tiempos típicos:

Paso Qué caza Cuánto tarda
Formato y estilo espacios, comillas, imports sin usar segundos
Chequeo de tipos el 80 % de los errores tontos ~1 min
Tests unitarios la lógica, sin base de datos ni red 1-2 min
Compilación lo que solo se rompe al construir 2-4 min
Tests de integración el sistema junto, de verdad 5-15 min

La regla que ordena todo se llama fallar temprano: cada paso corre solo si el anterior pasó. Si el chequeo de tipos falla, no tiene sentido gastar diez minutos en tests de integración — ese código no va a ningún lado igual.

Un ejemplo completo

Esto es un pipeline real y entero, para GitHub Actions. Otros servicios cambian los nombres, no las ideas.

name: CI
# Corre al subir a main y en cada propuesta de cambio hacia main.
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  verificar:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      # `cache: npm` guarda las dependencias entre corridas. Sin esto,
      # cada corrida las descarga de nuevo: suele ser el paso más lento.
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: npm

      # `npm ci` instala exactamente lo del lock. `npm install` puede
      # actualizar versiones sin avisar, y ahí el pipeline deja de ser
      # reproducible.
      - run: npm ci

      # De lo más rápido a lo más lento: cada paso corre solo si el
      # anterior pasó.
      - run: npm run lint
      - run: npm run check
      - run: npm test
      - run: npm run build

Si eso te parece poco, está bien: es casi todo lo que necesita un proyecto chico. Lo que no se ve en el archivo pero hay que configurar una sola vez en el repositorio es la parte que lo vuelve obligatorio: bloquear el merge cuando el pipeline falla. Sin eso es una sugerencia.

Un requisito previo que conviene decir en voz alta: esos cuatro comandos tienen que funcionar sobre una copia recién clonada, sin pasos manuales antes. Si en tu máquina andan porque tenés una variable de entorno cargada hace tres meses, en el runner van a fallar — de eso hablamos acá.

Cuánto cuesta esto

La pregunta que frena a mucha gente antes de empezar, y la respuesta corta es que para un proyecto chico, nada.

En GitHub Actions, los repositorios públicos no pagan minutos. Los privados vienen con una cuota mensual gratuita que alcanza de sobra para un proyecto personal: el pipeline de arriba consume unos cinco minutos por corrida, así que la cuota da para cientos de corridas al mes. GitLab y las demás plataformas tienen esquemas parecidos.

Lo que sí se paga, cuando se paga, es la falta de caché y los pasos lentos corriendo donde no corresponde. Guardar las dependencias entre corridas —esa línea cache: npm del ejemplo— y no mandar la suite completa en cada push son las dos cosas que mantienen el consumo bajo. La optimización, como siempre, recién cuando el número moleste.

Lo que no va en el pipeline

Tan importante como qué poner:

  • Tests contra servicios externos reales. Si tu pipeline llama a la API de un tercero, hereda las caídas de ese tercero. Un día falla sin que nadie haya tocado nada, y ahí empieza la desconfianza.
  • Cualquier cosa que tarde más de lo que alguien está dispuesto a esperar. Si la suite completa son cuarenta minutos, no va en cada push: va una vez por noche, o antes de desplegar.
  • Secretos escritos en el archivo. El YAML está versionado y es visible. Las claves van en el almacén de secretos del servicio y entran como variables.
  • Pasos que fallan seguido por motivos que no son el código. Este es el peor de todos, y merece su propio párrafo.

Un paso que falla una de cada cinco veces sin razón —un test que depende del reloj, una descarga inestable— es peor que no tener ese paso. Enseña al equipo que el rojo no significa nada, y el día que el rojo signifique algo de verdad, nadie va a mirar. Si un paso es inestable, se arregla o se saca; no se deja “para revisar después”.

De verde a producción

La otra mitad es el despliegue. La idea es la misma: que no dependa de que alguien se acuerde de los pasos.

Qué lo dispara. Lo más simple que funciona es que un merge a la rama principal, con el pipeline en verde, despliegue solo. Sin botón, sin ventana de despliegue, sin viernes prohibidos.

Un entorno intermedio. Antes de producción conviene un ambiente de prueba —lo que suele llamarse staging— que sea una copia del real y reciba los cambios primero. Sirve para ver el cambio funcionando con datos parecidos a los de verdad, que es distinto a ver los tests en verde.

Separar desplegar de activar. Un feature flag es un interruptor que deja el código nuevo publicado pero apagado, y lo prende cuando vos querés. Suena a complicación de más hasta la primera vez que apagás algo roto en diez segundos, en vez de desplegar a las apuradas una corrección.

Y la regla dura: si no sabés cómo volver atrás, eso no es un despliegue automatizado, es un salto al vacío automatizado. Antes de automatizar la ida, hay que saber cuánto tarda la vuelta, tenerla probada, y que no dependa de una persona en particular.

Lo mínimo que ya paga

No hace falta nada de lo anterior para empezar a ganar. Si vas a hacer una sola cosa, que sea esta: que los tests corran solos en cada propuesta de cambio y que el merge quede bloqueado si fallan.

Son diez líneas de configuración y media hora de trabajo, y cambia la pregunta que se hace el equipo. Se deja de discutir si alguien corrió las pruebas, porque ya no es algo que alguien pueda olvidarse: o están en verde, o no se mergea.

Comentarios