Els cursos de CI/CD solen fallar en el mateix punt: ensenyen pipelines sobre un "hola món" que no té base de dades, ni migracions, ni dues aplicacions que comparteixin codi, ni un equip amb opinions diferents. I després, en un projecte real, no encaixa res. Aquest curs fa el contrari: construirem, pas a pas, el pipeline complet d'una aplicació amb la complexitat justa perquè apareguin els problemes de debò. Aquesta aplicació es diu Reservalia. En aquesta lliçó la coneixerem a fons: què fa el producte, qui forma l'equip, com està organitzat el repositori, quines ordres existeixen ja —perquè el pipeline no inventa res, només invoca el que el projecte ja sap fer—, quins entorns hi ha i a quina infraestructura despleguem. Acabarem amb el full de ruta del curs aplicat a Reservalia i amb el que necessites instal·lat per seguir-lo. Encara no escriurem cap flux de treball: la primera peça real de pipeline es construeix a la lliçó 02-02.
Contingut
- Què és Reservalia
- L'equip: Marta, Diego i Nuria
- Com treballen avui (i per què fa mal)
- L'estructura del repositori
- Els
package.jsoni les ordres que el pipeline invocarà - L'entorn de desenvolupament local amb Docker Compose
- Els tres entorns: dev, staging i prod
- La infraestructura AWS de destinació
- El full de ruta del curs aplicat a Reservalia
- Com seguir el curs si no fas servir Node.js
- Què necessites instal·lat
- Errors comuns i consells
- Exercicis
- Conclusió
- Què és Reservalia
Reservalia és una plataforma SaaS de reserva de cites per a petits negocis de serveis: perruqueries, clíniques dentals, tallers mecànics, fisioterapeutes, centres d'estètica. És una empresa fictícia creada per a aquest curs; qualsevol semblança amb la realitat és intencionada però no real.
El producte té dues cares:
- El panell del negoci. El propietari de la perruqueria configura els seus serveis (tall, color, tractament), els seus professionals, els horaris d'obertura i les seves vacances. Consulta l'agenda del dia, confirma cites i veu estadístiques bàsiques.
- La pàgina pública de reserva. El client final entra des d'un enllaç, veu els forats lliures, en tria un i reserva sense registrar-se. Rep una confirmació per correu i un recordatori 24 hores abans.
Model de negoci: subscripció mensual per negoci, amb tres plans. Ara mateix tenen 340 negocis de pagament i processen unes 9.000 cites al mes.
1.1. Per què Reservalia és un bon projecte per aprendre CI/CD
No és un exemple de joguina, i això importa. Reservalia té exactament els ingredients que fan interessant un pipeline:
| Ingredient | Per què complica el pipeline | On el tractarem |
|---|---|---|
| Base de dades amb esquema evolutiu | Hi ha migracions per aplicar, i aplicar-les malament trenca producció o perd dades | 04-06 |
| Dues aplicacions en un mateix repositori | Es prova tot a cada canvi o només el que està afectat? | 04-04 |
| Codi compartit entre elles | Un canvi en tipus compartits afecta totes dues | 02-03 |
| Dades sensibles (noms, telèfons, correus de clients finals) | Els entorns no poden compartir dades reals; cal gestionar secrets | 04-03 |
| Horari comercial amb pics | No es pot desplegar de qualsevol manera a les 11:00 d'un dissabte | 03-04 |
| Tercers integrats (correu, passarel·la de pagament) | Cal decidir què se simula en proves i què no | 02-04 |
| Una aplicació mòbil (a partir del mòdul 5) | El "desplegament" passa per botigues d'aplicacions, amb les seves regles | 05-02 |
Si aconsegueixes automatitzar Reservalia, sabràs automatitzar el teu projecte.
- L'equip: Marta, Diego i Nuria
Tres persones, tres punts de vista. Tots tres apareixeran durant tot el curs, i les seves discussions són les que probablement tindràs tu amb el teu equip.
2.1. Marta — tech lead
Fa quatre anys que és a Reservalia i arrossega la responsabilitat que el producte funcioni. És qui decideix què es desplega i quan, i qui rep la trucada del comercial quan un client es queixa.
- Què li preocupa: que les incidències no es repeteixin i poder justificar davant de direcció el temps que l'equip dedica a "coses que no són funcionalitats".
- El seu paper al curs: aporta el punt de vista de negoci. És qui pregunta "quant costa això i què hi guanyem?" (la lliçó 01-02) i qui prémerà el botó d'aprovació a la porta manual del desplegament a producció.
- La seva frase: "Prefereixo desplegar deu vegades al dia i que cada desplegament m'importi ben poc, que desplegar un cop a la setmana i no dormir el dijous."
2.2. Diego — desenvolupador backend
Tres anys a l'empresa. Escriu la major part d'apps/api i és, avui, l'única persona que sap desplegar. Sap perfectament que el procés actual és dolent; el que no ha tingut mai és temps per arreglar-lo.
- Què li preocupa: que el pipeline el faci anar més lent. Li fa pànic un CI que trigui 40 minuts a dir-li si el seu canvi està bé.
- El seu paper al curs: és l'escèptic productiu. Cada vegada que afegim un pas al pipeline, en Diego preguntarà quant temps hi suma. Gràcies a ell, el pipeline final serà ràpid.
- La seva frase: "Si el CI triga més que anar a buscar un cafè, acabaré ignorant-lo."
2.3. Nuria — SRE
Es va incorporar fa vuit mesos, a mitja jornada compartida amb un altre producte. Va muntar la infraestructura d'AWS a mà, des de la consola web, perquè calia sortir a producció ja. Porta les guàrdies.
- Què li preocupa: que la infraestructura no estigui documentada enlloc tret del seu cap, i que un rollback avui signifiqui recompilar i tornar a pujar per SFTP amb clients esperant.
- El seu paper al curs: aporta la perspectiva d'operació. És qui insisteix en la infraestructura com a codi (03-03), en el rollback (03-05) i en el monitoratge (03-06).
- La seva frase: "Un desplegament que no es pot desfer en cinc minuts no és un desplegament, és una aposta."
- Com treballen avui (i per què fa mal)
Recordem el punt de partida, ara amb el detall complet del procés:
# El ritual del divendres a Reservalia. Durada: ~3 hores.
# Executor: en Diego, sempre en Diego.
# 1) Es baixa l ultim de main (sense saber exactament que hi entra)
git checkout main && git pull
# 2) Compila al seu portatil, amb LA SEVA versio de Node i ELS SEUS node_modules
cd apps/api && npm install && npm run build
cd ../web && npm install && npm run build
# 3) Puja els fitxers per SFTP a l unic servidor de produccio
sftp diego@reservalia-prod
# > put -r apps/api/dist/* /var/www/api/
# > put -r apps/web/dist/* /var/www/web/
# 4) Reinicia el proces a ma
ssh diego@reservalia-prod 'pm2 restart api'
# 5) Aplica les migracions enganxant SQL a psql
psql -h reservalia-prod-db -U admin -d reservalia
# > ALTER TABLE cites ADD COLUMN recordatori_enviat boolean DEFAULT false;
# 6) Comprova a ull que la web carrega i crea una cita de prova
# 7) Escriu al canal de l equip: "desplegat ✅"I els problemes d'aquest ritual, enumerats sense pietat:
| # | Problema | Conseqüència real que ja han patit |
|---|---|---|
| 1 | Build al portàtil d'en Diego | La build no és reproduïble; ningú no pot recrear el que hi ha en producció |
| 2 | npm install en comptes de npm ci |
Una versió menor d'una dependència va entrar en producció sense que ningú l'hagués provada |
| 3 | Desplegament incremental per SFTP | Fitxers de versions antigues continuen vius al servidor; l'estat real és desconegut |
| 4 | Finestra de desplegament sense servei | Hi ha uns segons en què l'API respon a mitges |
| 5 | Migracions a mà | Ningú no sap amb certesa quines migracions s'han aplicat ni en quin ordre |
| 6 | Sense registre de què es va desplegar | Davant d'una fallada, cal endevinar què va canviar |
| 7 | Verificació visual | Es va donar per bo un desplegament en què l'enviament de correus estava trencat |
| 8 | Bus factor d'1 | A les vacances d'en Diego a l'agost, no es va desplegar res durant tres setmanes |
| 9 | Rollback ≈ 1 hora | A l'última incidència, el servei va estar degradat 40 minuts |
| 10 | Divendres a la tarda | Dos caps de setmana espatllats el darrer trimestre |
Cada línia d'aquesta taula desapareixerà en algun moment del curs. Guarda-la: a la lliçó final (07-06) la repassarem sencera.
- L'estructura del repositori
Reservalia té un únic repositori a github.com/reservalia/reservalia, organitzat com a monorepo amb workspaces d'npm. Aquesta és la seva estructura tal com està avui, abans de començar:
reservalia/
├── .github/
│ └── workflows/ ← buit avui; aquí viuran els nostres pipelines
├── apps/
│ ├── api/ ← Node.js 20 + TypeScript + Express + PostgreSQL
│ │ ├── src/
│ │ │ ├── index.ts punt d'entrada del servidor
│ │ │ ├── rutes/
│ │ │ │ ├── cites.ts crear, llistar i cancel·lar cites
│ │ │ │ ├── negocis.ts alta i configuració de negocis
│ │ │ │ └── disponibilitat.ts càlcul de forats lliures
│ │ │ ├── domini/
│ │ │ │ ├── cita.ts regles de negoci d'una cita
│ │ │ │ └── agenda.ts solapaments, horaris, festius
│ │ │ └── db/
│ │ │ ├── client.ts connexió a PostgreSQL
│ │ │ └── migracions/
│ │ │ ├── 0001_crear_negocis.sql
│ │ │ ├── 0002_crear_cites.sql
│ │ │ └── 0003_afegir_recordatoris.sql
│ │ ├── tests/
│ │ │ ├── unitat/ ràpids, sense base de dades
│ │ │ └── integracio/ contra PostgreSQL real
│ │ ├── Dockerfile ← l'escriurem a 02-03
│ │ ├── package.json
│ │ └── tsconfig.json
│ │
│ └── web/ ← React + Vite + TypeScript
│ ├── src/
│ │ ├── main.tsx
│ │ ├── pagines/
│ │ │ ├── PanelNegoci.tsx
│ │ │ └── ReservaPublica.tsx
│ │ └── components/
│ ├── tests/
│ ├── Dockerfile ← l'escriurem a 02-03
│ ├── package.json
│ └── vite.config.ts
│
├── packages/
│ └── tipus-compartits/ ← tipus TypeScript usats per api i web
│ ├── src/index.ts
│ └── package.json
│
├── infra/ ← infraestructura com a codi (mòdul 3)
│ ├── terraform/
│ │ ├── moduls/
│ │ └── entorns/
│ │ ├── staging/
│ │ └── prod/
│ └── README.md
│
├── docker-compose.yml ← entorn de desenvolupament local
├── package.json ← arrel del monorepo, amb workspaces
├── package-lock.json ← un de sol per a tot el monorepo!
├── .nvmrc ← fixa la versió de Node
└── README.mdTres decisions d'aquesta estructura mereixen explicació, perquè condicionen tot el pipeline:
Monorepo amb un únic package-lock.json. En fer servir workspaces d'npm, hi ha un sol fitxer de bloqueig a l'arrel que governa les dependències d'api, web i tipus-compartits. Avantatge: un únic npm ci instal·la tot de manera coherent i reproduïble. Conseqüència per al pipeline: la instal·lació es fa una vegada a l'arrel, no una per aplicació.
packages/tipus-compartits. Aquí viuen els tipus que l'API i la web comparteixen (per exemple, la forma d'una Cita). És el que garanteix que si en Diego canvia el model de dades, la web deixi de compilar en CI en comptes de trencar-se en producció. També significa que un canvi en aquest paquet obliga a provar les dues aplicacions: és la complicació que fa interessant l'"executar només allò afectat" de la lliçó 04-04.
.nvmrc. Un fitxer d'una línia que diu quina versió de Node fa servir el projecte. És la peça més barata de reproduïbilitat que existeix:
Amb això, el portàtil d'en Diego, el de la Marta i el runner de CI fan servir exactament la mateixa versió. A la lliçó 01-01 vam veure que la versió no fixada és un dels enemics clàssics de la build reproduïble; aquest fitxer ho resol.
- Els
package.json i les ordres que el pipeline invocarà
package.json i les ordres que el pipeline invocaràAquí hi ha una idea central del curs, i convé subratllar-la:
El pipeline no inventa res. Només executa, en una màquina neta, les mateixes ordres que tu executes al teu portàtil.
Per això, abans d'escriure cap flux de treball, el projecte ha de tenir les seves ordres ben definides. Si npm test no funciona a la teva màquina, no funcionarà en CI. Si funciona a la teva màquina però només perquè tens una variable d'entorn que ningú més no té, fallarà en CI. Un pipeline és, sobretot, un revelador de suposicions ocultes.
5.1. package.json de l'arrel
{
"name": "reservalia",
"private": true,
"workspaces": [
"apps/*",
"packages/*"
],
"engines": {
"node": ">=20.11.0 <21"
},
"scripts": {
"build": "npm run build --workspaces --if-present",
"test": "npm run test --workspaces --if-present",
"lint": "npm run lint --workspaces --if-present",
"typecheck": "npm run typecheck --workspaces --if-present",
"dev": "docker compose up -d && npm run dev --workspace apps/api"
}
}Punt per punt, perquè cada línia té un motiu:
"private": trueevita que el paquet arrel es publiqui per accident al registre públic d'npm. En un monorepo és obligatori."workspaces"declara els subprojectes. En executarnpm cia l'arrel, npm instal·la les dependències de tots ells i crea els enllaços entretipus-compartitsi les aplicacions que el fan servir."engines"documenta la versió de Node admesa. Combinat amb.nvmrc, deixa constància explícita del requisit.--workspaces --if-presentexecuta l'script a cada subprojecte que el tingui definit, i no falla en els que no el tinguin. És el que permet que un solnpm testa l'arrel provi l'API, la web i el paquet compartit.
Amb això, el pipeline sencer es podria reduir a quatre ordres. Comprova-ho:
npm ci # installacio reproduible de tot el monorepo
npm run lint # estil i errors evidents
npm run typecheck # coherencia de tipus entre api, web i tipus-compartits
npm run test # proves de tots els subprojectes
npm run build # artefactes compilatsAquestes cinc ordres són, literalment, l'esquelet del pipeline que construirem al mòdul 2.
5.2. package.json d'apps/api
{
"name": "@reservalia/api",
"version": "0.0.0",
"private": true,
"scripts": {
"dev": "tsx watch src/index.ts",
"build": "tsc --project tsconfig.build.json",
"start": "node dist/index.js",
"test": "vitest run",
"test:unitat": "vitest run tests/unitat",
"test:integracio": "vitest run tests/integracio",
"lint": "eslint src tests --max-warnings 0",
"typecheck": "tsc --noEmit",
"migrate": "node dist/db/migrar.js",
"migrate:status": "node dist/db/migrar.js --status"
},
"dependencies": {
"@reservalia/tipus-compartits": "*",
"express": "4.19.2",
"pg": "8.11.5",
"zod": "3.23.8"
},
"devDependencies": {
"@types/express": "4.17.21",
"eslint": "8.57.0",
"tsx": "4.7.1",
"typescript": "5.4.5",
"vitest": "1.6.0"
}
}Detalls importants per al pipeline:
test:unitatitest:integracioestan separats. Les unitàries són ràpides i no necessiten base de dades; les d'integració necessiten un PostgreSQL aixecat. Aquesta separació és la que ens permetrà, a 02-04, donar retroalimentació ràpida a en Diego: primer les ràpides, i només si passen, les lentes.--max-warnings 0al lint. Sense això, ESLint acaba amb codi de sortida 0 encara que hi hagi avisos, i el pipeline es posaria verd amb problemes a dins. Recorda de 01-03: el pipeline només entén de codis de sortida.typecheckseparat debuild.tsc --noEmitcomprova els tipus sense generar fitxers. És ràpid i pot córrer en paral·lel amb els tests.migrateexisteix com a script. Avui en Diego enganxa SQL a mà; el projecte ja té l'ordre, només que ningú no la fa servir. A la lliçó 04-06 la integrarem al desplegament.- Versions exactes, sense
^."express": "4.19.2"i no"^4.19.2". Juntament ambnpm ci, garanteix que dues instal·lacions donin el mateix. L'estratègia completa de dependències és matèria de 04-02.
5.3. package.json d'apps/web
{
"name": "@reservalia/web",
"version": "0.0.0",
"private": true,
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"test": "vitest run",
"lint": "eslint src tests --max-warnings 0",
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@reservalia/tipus-compartits": "*",
"react": "18.3.1",
"react-dom": "18.3.1"
},
"devDependencies": {
"@vitejs/plugin-react": "4.2.1",
"typescript": "5.4.5",
"vite": "5.2.11",
"vitest": "1.6.0"
}
}Fixa't que els noms dels scripts coincideixen amb els de l'API: build, test, lint, typecheck. Aquesta convenció no és cosmètica: és el que fa que npm run test --workspaces funcioni i el que permetrà que el pipeline tracti totes dues aplicacions amb la mateixa lògica.
Consell transferible: unificar els noms dels scripts entre subprojectes és una de les inversions més rendibles abans de muntar un pipeline. Si en un projecte l'ordre és
npm testi en un altrenpm run test:ci, el teu YAML s'omplirà de casos especials.
- L'entorn de desenvolupament local amb Docker Compose
Perquè les proves d'integració funcionin —tant al portàtil com després en CI— cal un PostgreSQL. Reservalia l'aixeca amb Docker Compose:
# docker-compose.yml — entorn de desenvolupament local de Reservalia
services:
db:
image: postgres:16.3 # versio FIXADA, igual que a RDS
container_name: reservalia-db
environment:
POSTGRES_USER: reservalia
POSTGRES_PASSWORD: desenvolupament # ⚠️ nomes local, mai en un altre entorn
POSTGRES_DB: reservalia
ports:
- "5432:5432" # accessible des del portatil
volumes:
- dades-db:/var/lib/postgresql/data
healthcheck: # esta la base de dades LLESTA, no nomes arrencada?
test: ["CMD-SHELL", "pg_isready -U reservalia -d reservalia"]
interval: 5s
timeout: 3s
retries: 10
mailpit:
image: axllent/mailpit:v1.18 # captura els correus en local
ports:
- "1025:1025" # l SMTP al qual apunta l API
- "8025:8025" # interficie web per llegir-los
volumes:
dades-db:Quatre coses per explicar, perquè totes reapareixeran al pipeline:
La versió està fixada: postgres:16.3. No postgres:latest. Si en local desenvolupes contra PostgreSQL 16 i producció corre 15, tard o d'hora una consulta funcionarà a la teva màquina i fallarà en producció. La versió de la base de dades és part de la reproduïbilitat.
El healthcheck és la línia més important del fitxer. Un contenidor de PostgreSQL "arrencat" no significa "llest per acceptar connexions": hi ha uns segons d'inicialització. Sense healthcheck, les proves d'integració fallen de manera intermitent perquè la base de dades encara no accepta connexions. I això és exactament una prova flaky, el verí del qual parlàvem a 01-02. La causa número u de flakiness en CI és no esperar que els serveis estiguin realment llestos.
Mailpit substitueix el proveïdor de correu real. Reservalia envia confirmacions i recordatoris. En local i en proves, aquests correus no han de sortir a internet: Mailpit els captura i els mostra en una web. La regla general que aplicarem a 02-04: en proves, cap servei extern real.
La contrasenya desenvolupament està escrita en clar, i està bé. Perquè és una base de dades local, efímera i sense dades reals. A staging i prod no ho farem mai: allà les credencials vénen d'un gestor de secrets. Distingir quan un valor és un secret i quan no és part de la matèria de 04-03.
El flux de treball diari d'en Diego, en quatre ordres:
git clone https://github.com/reservalia/reservalia.git
cd reservalia
nvm use # llegeix .nvmrc → Node 20.11.0
npm ci # installa tot el monorepo de manera reproduible
docker compose up -d # aixeca PostgreSQL i Mailpit
npm run dev # arrenca l API en mode desenvolupamentQuan al mòdul 2 configurem el CI, veuràs que el pipeline fa exactament el mateix: preparar Node, instal·lar dependències, aixecar els serveis necessaris i executar ordres. No hi ha màgia.
- Els tres entorns: dev, staging i prod
Un entorn (definit a 01-01) és una instància desplegada i executable del sistema, amb la seva pròpia configuració i les seves pròpies dades. Reservalia en tindrà tres:
| Aspecte | dev |
staging |
prod |
|---|---|---|---|
| Per a què serveix | Que l'equip provi canvis en un entorn compartit i real | Assaig general: la darrera verificació abans de producció | Els clients de debò |
| Qui el fa servir | Marta, Diego, Nuria | L'equip, abans d'aprovar un desplegament | 340 negocis i els seus clients |
| Què s'hi desplega | Cada merge a main, automàticament |
Cada merge a main, després de passar dev |
Només després d'aprovació manual de la Marta |
| Dades | Dades fictícies generades, es poden esborrar | Dades fictícies amb volum realista | Dades reals de clients |
| Base de dades | RDS db.t4g.micro |
RDS db.t4g.micro |
RDS db.t4g.medium, multi-AZ i amb còpies de seguretat |
| Instàncies de l'API | 1 | 1 | 2 com a mínim, amb escalat automàtic |
| Correu | Capturat, no surt | Capturat, no surt | Proveïdor real |
| Passarel·la de pagament | Mode simulat | Mode simulat | Mode real |
| Qui hi pot accedir | Només l'equip | Només l'equip | Públic |
| Si cau | No passa res | No passa res | Incident |
Dos principis que regeixen els tres entorns i que convé interioritzar ja:
Principi 1: el mateix artefacte als tres. És la regla de promoció de 01-01. La imatge reservalia/api:a3f9c21 que es prova a dev és exactament la mateixa que arriba a staging i a prod. L'única cosa que canvia entre entorns és la configuració injectada per fora:
# dev
DATABASE_URL=postgres://[email protected]:5432/reservalia
LOG_LEVEL=debug
PASSARELLA_PAGAMENT_MODE=simulat
SMTP_HOST=mailpit.intern
# prod
DATABASE_URL=postgres://[email protected]:5432/reservalia
LOG_LEVEL=info
PASSARELLA_PAGAMENT_MODE=real
SMTP_HOST=smtp.proveidor-correu.comPrincipi 2: les dades de producció no surten mai de producció. Copiar la base de dades de prod a staging "per provar amb dades reals" és una pràctica freqüent i una molt mala idea: Reservalia desa noms, telèfons i correus de clients finals. staging fa servir dades generades amb volum realista, no dades reals.
- La infraestructura AWS de destinació
La Nuria va muntar això a mà des de la consola d'AWS. Al mòdul 3 ho passarem a Terraform perquè sigui reproduïble; de moment, aquesta és la foto d'on desplega el pipeline:
flowchart TB
U["👥 Usuaris<br/>negocis i els seus clients"] --> CF["CloudFront + S3<br/>apps/web estatic"]
U --> ALB["Application Load Balancer<br/>api.reservalia.com"]
subgraph AWS["AWS · regio eu-west-1"]
ALB --> ECS
subgraph ECS["ECS Fargate · servei reservalia-api"]
T1["Tasca 1<br/>contenidor api:a3f9c21"]
T2["Tasca 2<br/>contenidor api:a3f9c21"]
end
ECS --> RDS[("RDS PostgreSQL 16<br/>multi-AZ en prod")]
ECS --> SM["Secrets Manager<br/>credencials de BD i APIs"]
ECS --> CW["CloudWatch Logs"]
ECR[("ECR<br/>registre d imatges")] -.->|"descarrega la imatge<br/>en desplegar"| ECS
end
GHA["⚙️ GitHub Actions"] -->|"1· publica la imatge"| ECR
GHA -->|"2· actualitza el servei"| ECS
GHA -->|"3· puja els estatics"| CF
style ECR fill:#d9f2d9
style GHA fill:#cfe8ff
Què fa cada peça i per què hi és:
| Peça | Funció | Per què aquesta i no una altra |
|---|---|---|
| ECR (Elastic Container Registry) | Desa les imatges Docker etiquetades amb el SHA del commit | És el registre d'artefactes: la peça que fa possible construir una vegada i promocionar |
| ECS Fargate | Executa els contenidors de l'API sense administrar servidors | Orquestració sense la complexitat de Kubernetes: proporcional a un equip de tres persones (vegeu 01-03) |
| ALB (Application Load Balancer) | Reparteix el trànsit entre les tasques i comprova la seva salut | Permet desplegaments sense tall: retira una tasca vella només quan la nova respon correctament |
| RDS PostgreSQL | La base de dades gestionada | Còpies de seguretat, pedaços i alta disponibilitat sense feina manual |
| S3 + CloudFront | Serveixen la web compilada com a fitxers estàtics | La web de Vite és HTML, CSS i JS: no necessita servidor |
| Secrets Manager | Desa credencials i les injecta en temps d'execució | Els secrets no viatgen a la imatge ni al repositori (04-03) |
| CloudWatch Logs | Recull els registres (log) de les tasques | Base per al monitoratge de 03-06 |
I el recorregut complet d'un canvi, del portàtil d'en Diego al client final:
sequenceDiagram
participant D as Diego
participant GH as GitHub
participant GA as GitHub Actions
participant ECR as ECR
participant ECS as ECS Fargate
participant U as Usuari
D->>GH: push a branca + pull request
GH->>GA: trigger del pipeline
GA->>GA: npm ci · lint · typecheck · test
GA-->>GH: ✅ verd, es pot fusionar
D->>GH: merge a main
GH->>GA: trigger del pipeline de desplegament
GA->>GA: docker build → imatge etiquetada amb el SHA
GA->>ECR: push de reservalia/api:a3f9c21
GA->>ECS: desplegar a dev i staging
GA->>GA: proves de fum a staging
GA-->>GH: ⏸️ esperant aprovacio de la Marta
Note over GA: porta manual = Lliurament Continu
GA->>ECS: promocionar el MATEIX artefacte a prod
ECS->>U: nova versio servint transit
Aquest diagrama és l'objectiu del curs. En acabar el mòdul 3, Reservalia tindrà exactament aquest flux funcionant.
- El full de ruta del curs aplicat a Reservalia
Què haurà canviat a Reservalia al final de cada mòdul:
| Mòdul | Què haurem automatitzat | Estat del ritual del divendres |
|---|---|---|
| 1. Introducció (ets aquí) | Res encara: vocabulari, criteris, projecte i mètriques de partida | Intacte: 3 h, manual |
| 2. Integració Contínua | Cada PR executa npm ci, lint, typecheck, tests unitaris i d'integració contra PostgreSQL. Es construeixen imatges Docker etiquetades amb el SHA i es publiquen a ECR. Es protegeix main perquè res no hi entri en vermell |
El desplegament continua sent manual, però ja no arriba res trencat a main. En Diego deixa de compilar a cegues |
| 3. Desplegament Continu | Desplegament automàtic a dev i staging; porta manual per a prod; infraestructura en Terraform; desplegament progressiu; feature flags; rollback automàtic; monitoratge |
El ritual del divendres desapareix. Es desplega quan cal, en minuts, i es pot desfer |
| 4. Pràctiques avançades | Pipeline reutilitzable i provat; gestió de dependències i actualitzacions; escaneig de seguretat i gestió de secrets; optimització de temps i cost; migracions de base de dades segures i reversibles | En Diego deixa de tocar psql per sempre. El pipeline baixa de 14 a menys de 8 minuts |
| 5. Projectes reals | S'incorpora apps/mobile (React Native) amb el seu propi flux cap a les botigues; s'estudia la separació en microserveis i com modernitzar un legacy |
El pipeline admet tres aplicacions amb necessitats diferents |
| 6. Eines | El mateix pipeline reexpressat en Jenkins, GitLab CI, CircleCI i Travis CI; contenidors i Kubernetes; GitHub Actions a fons | El coneixement de l'equip deixa de dependre d'una eina |
| 7. Exercicis | Reconstrucció pràctica de tot, de principi a fi, pel teu compte | Ho saps fer tu, no només llegir-ho |
| 8. Recursos | Rutes d'aprenentatge, comunitats, certificacions | — |
- Com seguir el curs si no fas servir Node.js
Reservalia fa servir Node.js i TypeScript, però el curs no tracta sobre Node.js. Tracta sobre pipelines. Si el teu dia a dia és Python, Java, Go, PHP, Ruby o .NET, tot el que aprenguis s'aplica igual: l'única cosa que canvia són les ordres concretes de cada pas.
Aquí tens la taula de traducció. Guarda-la:
| Pas del pipeline | Node.js (el curs) | Python | Java (Maven) | Go | PHP | .NET |
|---|---|---|---|---|---|---|
| Fixar la versió | .nvmrc |
.python-version |
pom.xml + toolchain |
go.mod |
composer.json |
global.json |
| Instal·lar dependències | npm ci |
pip install -r requirements.txt |
mvn dependency:go-offline |
go mod download |
composer install --no-dev |
dotnet restore |
| Fitxer de bloqueig | package-lock.json |
requirements.txt fixat / poetry.lock |
pom.xml amb versions exactes |
go.sum |
composer.lock |
packages.lock.json |
| Anàlisi estàtica | eslint |
ruff / flake8 |
checkstyle / spotbugs |
go vet |
phpstan |
dotnet format |
| Comprovació de tipus | tsc --noEmit |
mypy |
(el compilador) | (el compilador) | phpstan |
(el compilador) |
| Executar proves | npm test (vitest) |
pytest |
mvn test |
go test ./... |
phpunit |
dotnet test |
| Construir | npm run build |
(empaquetat o imatge) | mvn package |
go build |
(imatge) | dotnet publish |
| Artefacte resultant | imatge Docker | imatge Docker / wheel | .jar o imatge |
binari o imatge | imatge | imatge |
| Migracions de BD | script migrate |
alembic upgrade head |
flyway migrate |
migrate up |
doctrine:migrations:migrate |
dotnet ef database update |
El que és idèntic en tots els llenguatges —i és la immensa majoria del curs:
- Els conceptes: trigger, job, etapa, runner, artefacte, entorn, promoció.
- El criteri d'èxit o fracàs: el codi de sortida de cada ordre.
- L'estructura del pipeline: instal·lar → verificar → construir → publicar → desplegar.
- L'estratègia d'entorns i la regla de construir una vegada i promocionar.
- Els contenidors: un
Dockerfilede Python i un de Node s'assemblen moltíssim. - Les estratègies de desplegament, el rollback, els feature flags i el monitoratge.
- Les mètriques DORA i tot el relatiu a seguretat i secrets.
Suggeriment pràctic: si vols treure'n el màxim partit, adapta cada exercici al teu propi projecte. Quan a 02-02 configurem npm ci && npm test, munta en paral·lel l'equivalent amb pip install && pytest en un repositori teu. Aprendràs el doble.
- Què necessites instal·lat
Per llegir el curs i entendre els exemples: res. Tots els fitxers són complets a les lliçons.
Per reproduir els exemples, que és molt recomanable:
| Eina | Per a què | Com comprovar que la tens |
|---|---|---|
| Git | Clonar, ramificar, fer commits | git --version |
| Compte de GitHub | Allotjar el teu repositori i executar Actions (gratis en repositoris públics) | — |
| Node.js 20 | Executar el projecte d'exemple | node --version |
| Docker + Docker Compose | PostgreSQL local i construcció d'imatges | docker --version i docker compose version |
| Un editor amb suport YAML | Escriure fluxos de treball sense barallar-te amb la indentació | — |
Opcionals, i només a partir del mòdul 3:
| Eina | Per a què | Nota |
|---|---|---|
| Compte d'AWS | Desplegar de debò a ECS i RDS | Té cost. Pots seguir el mòdul 3 sense ell: tots els fitxers són llegibles i aplicables després |
| AWS CLI | Interactuar amb AWS des de la terminal | aws --version |
| Terraform o OpenTofu | Infraestructura com a codi | terraform --version |
Comprovació ràpida del teu entorn:
# Executa aixo i comprova que no falta res essencial.
echo "--- Imprescindible ---"
git --version || echo "❌ falta Git"
node --version || echo "❌ falta Node.js"
docker --version || echo "❌ falta Docker"
docker compose version || echo "❌ falta Docker Compose"
echo "--- Opcional (modul 3 endavant) ---"
aws --version || echo "ℹ️ AWS CLI no installat (opcional)"
terraform --version || echo "ℹ️ Terraform no installat (opcional)"Si no pots instal·lar res (per exemple, en un ordinador corporatiu restringit): pots seguir pràcticament tot el curs creant un repositori a GitHub des del navegador i editant els fluxos de treball des de la interfície web. Els runners de GitHub executen al núvol. És menys còmode, però funciona.
Errors Comuns i Consells
Error 1: voler automatitzar abans que les ordres funcionin en local. Si npm test no funciona al teu portàtil, no funcionarà en CI. El pipeline és un revelador de suposicions ocultes: variables d'entorn que només tens tu, serveis que estan arrencats a la teva màquina des de fa setmanes, fitxers que no vas pujar mai al repositori. Primer fes que funcioni en una màquina neta; després automatitza-ho.
Error 2: no fixar les versions de les eines. .nvmrc, engines, postgres:16.3 en comptes de postgres:latest, dependències sense ^. Cada versió no fixada és una build que algun dia deixarà de funcionar sense que ningú hagi tocat res, i aquest és el tipus de fallada més cara de diagnosticar.
Error 3: noms d'scripts diferents a cada subprojecte. Si apps/api fa servir npm test i apps/web fa servir npm run test:ci, el teu pipeline s'omplirà de casos especials. Unifica els noms abans d'escriure el primer flux de treball: és mitja hora de feina que estalvia dies.
Error 4: copiar la base de dades de producció a staging. És temptador ("provem amb dades reals") i és una fuita de dades personals esperant a passar. Reservalia desa telèfons i correus de clients finals. Genera dades fictícies amb volum realista.
Error 5: oblidar el healthcheck dels serveis auxiliars. "Contenidor arrencat" no és "servei llest". És la causa número u de tests intermitents en CI, i com vam veure a 01-02, els tests flaky destrueixen la confiança en el pipeline sencer.
Consell 1: si tens un projecte propi, fes-lo servir en paral·lel. Aplica cada lliçó a Reservalia i també al teu projecte. La transferència de coneixement és molt més gran quan t'enfrontes a les particularitats del teu propi codi.
Consell 2: escriu avui la teva pròpia taula del ritual del divendres. Enumera tots els passos manuals del teu procés de desplegament actual, amb la seva durada. És una llista incòmoda de llegir i és exactament el mapa del que automatitzaràs.
Consell 3: un README.md que funcioni és el primer pas de CI. Si un desenvolupador nou pot clonar el repositori i tenir el projecte funcionant seguint el README sense preguntar a ningú, el teu projecte està llest per automatitzar-se. Si no, arregla això primer: el pipeline és exactament aquell README executat per una màquina.
Exercicis
Exercici 1: deduir el pipeline a partir del projecte
Sense escriure gens de YAML —encara no toca—, i fent servir únicament el que saps del repositori de Reservalia, respon:
- En quin ordre executaries
npm ci,lint,typecheck,test:unitat,test:integracioibuild? Justifica l'ordre. - Quins d'aquests passos es poden executar en paral·lel i quins obligatòriament en sèrie?
- Quin pas necessita que hi hagi un PostgreSQL aixecat, i què implica això per al runner?
- Un canvi toca només
apps/web/src/pagines/PanelNegoci.tsx. Seria correcte executar únicament les proves d'apps/web? I si el canvi toquéspackages/tipus-compartits/src/index.ts?
Exercici 2: dissenyar la matriu de configuració per entorn
Reservalia ha de decidir, per a cada valor de configuració, si és un secret (va al gestor de secrets), una variable d'entorn normal (pot ser al repositori) o una cosa que no ha d'existir en aquell entorn. Completa la taula i justifica els tres casos que et semblin menys evidents:
| Valor | dev |
staging |
prod |
|---|---|---|---|
DATABASE_URL |
|||
| Contrasenya de la base de dades | |||
LOG_LEVEL |
|||
| Clau d'API de la passarel·la de pagament | |||
SMTP_HOST |
|||
| Nom del repositori d'ECR | |||
| Clau de signatura dels tokens de sessió |
Exercici 3: traduir Reservalia a la teva tecnologia
Imagina't que Reservalia estigués escrita en Python amb FastAPI en comptes de Node.js amb Express. Escriu:
- L'estructura equivalent d'
apps/api/(noms de fitxers de configuració i de gestió de dependències). - L'equivalent de les cinc ordres del pipeline (
npm ci,lint,typecheck,test,build). - Quines parts del
docker-compose.ymlcanviarien i quines no. - Quines parts del curs deixarien d'aplicar. (Pista: poques.)
Solucions
Solució a l'Exercici 1
1. Ordre i justificació
1. npm ci ← imprescindible primer: sense dependencies no hi ha res
2. lint · typecheck ← rapids, detecten errors evidents, sense infraestructura
3. test:unitat ← rapids, sense base de dades
4. test:integracio ← lents, requereixen PostgreSQL
5. build ← nomes te sentit si tot l anterior es verdEl principi que governa aquest ordre s'anomena fail fast: col·loca primer el més ràpid i el que més probablement falli. Si en Diego s'ha deixat un console.log o un tipus incompatible, vols que ho sàpiga en 40 segons, no després d'esperar sis minuts a les proves d'integració. Ordenar el pipeline al revés funciona igual de bé tècnicament, però malbarata el temps de l'equip a cada fallada.
2. Paral·lel davant de sèrie
npm ciha d'anar primer i sol: tot en depèn.lint,typecheckitest:unitatpoden anar en paral·lel entre si: són independents, no comparteixen estat i cap no necessita el resultat de l'altre.test:integraciopot anar en paral·lel amb els anteriors si el runner pot aixecar PostgreSQL alhora, encara que se sol executar després per no pagar el cost d'aixecar la base de dades quan alguna cosa trivial ja ha fallat. És una decisió de compromís entre velocitat i cost.buildva al final: construir un artefacte de codi que no passa les proves és temps i diners llençats.
Un matís sobre typecheck en aquest monorepo concret: com que apps/web depèn de packages/tipus-compartits, la comprovació de tipus de la web pot requerir que el paquet compartit estigui compilat. En projectes amb referències de projecte de TypeScript això es resol sol; en d'altres, obliga a un build parcial previ. És el tipus de detall que descobreixes en muntar el pipeline i que resoldrem a 02-03.
3. PostgreSQL al runner
test:integracio el necessita. Implicacions per al runner:
- El runner ha de poder executar contenidors (els runners allotjats de GitHub poden).
- Cal esperar que la base de dades estigui llesta, no només arrencada: healthcheck o espera activa. Sense això, tests flaky garantits.
- Cal aplicar les migracions abans d'executar les proves, perquè l'esquema existeixi.
- Cada execució ha de partir d'una base de dades neta, o l'estat d'una prova contaminarà la següent.
4. Execució selectiva
- Canvi només a
apps/web/src/pagines/PanelNegoci.tsx: sí, seria raonable executar únicament les proves d'apps/web. L'API no pot veure's afectada per un canvi en un component de React. Això és execució selectiva i és una de les principals palanques d'optimització (04-04). - Canvi a
packages/tipus-compartits/src/index.ts: no. Aquest paquet és una dependència de les dues aplicacions, així que cal provar totes dues. Aquest és precisament el cas que fa perillosa l'execució selectiva mal implementada: si la teva regla és "només provo la carpeta que ha canviat", un canvi al paquet compartit no provaria res i podria trencar les dues aplicacions alhora. L'execució selectiva s'ha de basar en el graf de dependències, no en la ruta del fitxer.
Solució a l'Exercici 2
| Valor | dev |
staging |
prod |
|---|---|---|---|
DATABASE_URL (sense contrasenya) |
Variable normal | Variable normal | Variable normal |
| Contrasenya de la base de dades | Secret | Secret | Secret |
LOG_LEVEL |
Variable normal (debug) |
Variable normal (debug) |
Variable normal (info) |
| Clau d'API de la passarel·la de pagament | Secret (clau de proves) | Secret (clau de proves) | Secret (clau real) |
SMTP_HOST |
Variable normal | Variable normal | Variable normal |
| Nom del repositori d'ECR | Variable normal | Variable normal | Variable normal |
| Clau de signatura de tokens de sessió | Secret | Secret | Secret, diferent de les altres dues |
Els tres casos menys evidents:
DATABASE_URL. La clau està a separar la cadena de connexió de la contrasenya. postgres://[email protected]:5432/reservalia no conté cap secret: és un nom d'amfitrió intern que, sense credencials i sense accés de xarxa, no serveix de res. La contrasenya s'injecta a part. Ficar la contrasenya dins de l'URL és còmode i converteix un valor públic en un secret, amb tot el que això implica: no es pot registrar en logs, no es pot posar al repositori, no es pot mostrar en un missatge d'error.
Clau de la passarel·la de pagament a dev i staging. Encara que siguin claus d'entorn de proves i no moguin diners reals, continuen sent secrets: permeten fer crides en nom de Reservalia, consultar dades i esgotar quotes. Una clau de proves filtrada en un repositori públic és un incident de seguretat menor, però és un incident. Regla pràctica: si el proveïdor l'anomena "clau secreta", és un secret.
Clau de signatura de tokens: diferent a cada entorn. Aquest és el cas més subtil i el més important. Si staging i prod compartissin la clau de signatura, un token emès a staging —on l'equip té accés total i pot crear l'usuari que vulgui— seria vàlid en producció. És una escalada de privilegis de manual. Els secrets criptogràfics no es comparteixen mai entre entorns, ni tan sols "temporalment per provar".
Un criteri general per classificar: si en filtrar-se permet a algú fer una cosa que no hauria de poder fer, és un secret. Si només revela com es diuen les coses, és configuració. Hi tornarem a 04-03.
Solució a l'Exercici 3
1. Estructura equivalent en Python/FastAPI
apps/api/
├── src/
│ ├── main.py punt d'entrada (en comptes d'index.ts)
│ ├── rutes/
│ ├── domini/
│ └── db/
│ └── migracions/ gestionades per Alembic
├── tests/
│ ├── unitat/
│ └── integracio/
├── pyproject.toml ← equivalent a package.json
├── poetry.lock ← equivalent a package-lock.json
├── .python-version ← equivalent a .nvmrc
├── alembic.ini configuració de migracions
└── Dockerfile2. Les cinc ordres
| Pas | Node.js | Python/FastAPI |
|---|---|---|
| Instal·lar | npm ci |
poetry install --sync (respecta el lock, com npm ci) |
| Lint | npm run lint |
ruff check src tests |
| Tipus | npm run typecheck |
mypy src |
| Proves | npm test |
pytest |
| Construir | npm run build |
No hi ha compilació: el "build" és directament docker build |
| Migracions | npm run migrate |
alembic upgrade head |
Una observació interessant: Python no té pas de compilació, així que l'artefacte es produeix directament en construir la imatge Docker. El pipeline té un pas menys, però la resta de l'estructura és idèntica.
3. Canvis al docker-compose.yml
- No canvia res del servei
db: PostgreSQL 16.3, el seu healthcheck, el seu volum i les seves variables són exactament iguals. La base de dades no sap en quin llenguatge està escrita l'aplicació. - No canvia res del servei
mailpit: captura SMTP, sigui qui sigui l'emissor. - Canviaria únicament el servei de l'aplicació, si se n'afegís un: la imatge base (
python:3.12-slimen comptes denode:20-slim) i l'ordre d'arrencada (uvicorn src.main:app --reload).
És un bon indicador de quant d'aquest curs és independent del llenguatge: la infraestructura de desenvolupament és pràcticament la mateixa.
4. Quines parts del curs deixarien d'aplicar
Gairebé cap. Concretament:
- Deixa d'aplicar: les ordres exactes d'npm, la sintaxi de
package.jsoni els detalls de les eines concretes de JavaScript (vitest, ESLint, tsc). - Continua aplicant íntegrament: tot el vocabulari de 01-01; els beneficis i costos de 01-02; el mapa d'eines de 01-03; les mètriques DORA de 01-05; tot el mòdul 3 (estratègies de desplegament, IaC, feature flags, rollback, monitoratge); tot el mòdul 4 tret dels exemples de sintaxi; el mòdul 6 complet; i l'estructura, l'ordre i la lògica de cada pipeline que construïm.
Estimació honesta: més del 85 % del curs és independent del llenguatge. El que canvia són les cadenes de text dins dels passos run:.
Conclusió
Ja coneixes el projecte sobre el qual treballarem durant tot el curs:
- Reservalia és una plataforma SaaS de reserva de cites amb 340 negocis de pagament, dues aplicacions (
apps/apien Node.js + Express + PostgreSQL iapps/weben React + Vite), un paquet de tipus compartits i un monorepo amb workspaces d'npm. - L'equip són la Marta (tech lead, mira el negoci i el risc), en Diego (backend, vol un CI ràpid i és l'únic que sap desplegar) i la Nuria (SRE, vol infraestructura reproduïble i rollback fiable). Les seves tensions són les que tindràs al teu equip.
- El projecte ja té les seves ordres definides:
npm ci,lint,typecheck,test,buildimigrate. Aquesta és la idea clau de la lliçó: el pipeline no inventa res, només executa en una màquina neta el que el projecte ja sap fer. Un pipeline és un revelador de suposicions ocultes. - Hi ha tres entorns —
dev,stagingiprod— governats per dos principis: el mateix artefacte es promociona als tres, i les dades de producció no surten mai de producció. - La infraestructura de destinació és AWS: imatges a ECR, execució a ECS Fargate darrere d'un balancejador, dades a RDS PostgreSQL, secrets a Secrets Manager i registres (log) a CloudWatch.
- I saps com seguir el curs amb qualsevol tecnologia: més del 85 % és independent del llenguatge; només canvien les ordres dins de cada pas.
Ens queda una última cosa abans de començar a construir. La Marta demanarà temps de l'equip per muntar tot això, i d'aquí a sis mesos algú preguntarà si ha servit d'alguna cosa. Per respondre a això cal haver mesurat el punt de partida abans de tocar res. A la lliçó següent, Mètriques DORA: Com es Mesura el Lliurament de Programari, veurem les quatre mètriques estàndard del sector —freqüència de desplegament, lead time for changes, change failure rate i time to restore service—, com calcular-les a partir de dades que el mateix pipeline genera, i establirem el quadre de comandament inicial de Reservalia: on són avui i on volen arribar al final del curs.
Curs de CI/CD: Integració i Desplegament Continu
Mòdul 1: Introducció al CI/CD
- Conceptes Bàsics de CI/CD
- Beneficis del CI/CD
- Eines Populars de CI/CD
- El Projecte del Curs: l'Aplicació que Automatitzarem
- Mètriques DORA: Com es Mesura el Lliurament de Programari
Mòdul 2: Integració Contínua (CI)
- Introducció a la Integració Contínua
- Configuració d'un Entorn de CI
- Automatització de la Construcció
- Proves Automatitzades
- Qualitat de Codi i Anàlisi Estàtica
- Artefactes, Versionat i Promoció
- Integració amb el Control de Versions
Mòdul 3: Desplegament Continu (CD)
- Introducció al Desplegament Continu
- Automatització del Desplegament
- Infraestructura com a Codi i Entorns Reproduïbles
- Estratègies de Desplegament
- Feature Flags, Rollback i Recuperació davant Errors
- Monitoratge i Retroalimentació
Mòdul 4: Pràctiques Avançades de CI/CD
- Pipelines de CI/CD
- Gestió de Dependències
- Seguretat en CI/CD
- Escalabilitat i Rendiment
- Pipeline as Code: Plantilles, Reutilització i Proves del Pipeline
- Bases de Dades al Pipeline: Migracions Segures
Mòdul 5: Implementació de CI/CD en Projectes Reals
- Cas d'Estudi: Projecte Web
- Cas d'Estudi: Aplicació Mòbil
- Cas d'Estudi: Microserveis
- Cas d'Estudi: Modernitzar un Projecte Legacy
Mòdul 6: Eines i Tecnologies
- Jenkins
- GitLab CI/CD
- CircleCI
- Travis CI
- Docker i Kubernetes
- GitHub Actions a Fons
- Comparativa i Criteris per Triar Eina
Mòdul 7: Exercicis Pràctics
- Exercici 1: Configuració d'un Pipeline Bàsic
- Exercici 2: Integració de Proves Automatitzades
- Exercici 3: Desplegament en un Entorn de Producció
- Exercici 4: Monitoratge i Retroalimentació
- Exercici 5: Enfortir el Pipeline amb Seguretat i Secrets
- Projecte Final: Pipeline Complet d'Extrem a Extrem
