La lliçó anterior va donar a Reservalia estratègies perquè el relleu de versions sigui suau, però totes s'apuntalen en la mateixa suposició optimista: que la fallada es nota mentre el desplegament és en marxa. Moltes no. Apareixen tres hores després, quan el canary ja s'ha promocionat, o només per als negocis d'un país concret, o només el primer dia de mes. A més arrosseguem un problema anterior: avui a Reservalia desplegar i llançar són la mateixa acció, així que una funcionalitat a mig fer no pot arribar a main. Aquesta lliçó separa aquestes dues coses amb feature flags, construeix el botó de tornada enrere de Reservalia i converteix els 68 minuts de time to restore en un objectiu de deu.
Contingut
- Separar el desplegament del llançament
- Tipus de feature flag, vida esperada i propietari
- Una implementació mínima a
apps/api - Fusionar codi incomplet a
mainsense branques de vida llarga - El deute de flags: per què caduquen i com es retiren
- Rollback d'artefacte davant de roll-forward
- El límit dur: la base de dades
- El botó de tornada enrere:
rollback.yml - Rollback automàtic disparat per mètriques
- Recuperació d'incidents: mitigar primer, diagnosticar després
- Post-mortem sense culpables
- De 68 minuts a menys de 10
- Errors Comuns i Consells
- Exercicis
- Conclusió
- Separar el desplegament del llançament
Són dos actes diferents que el costum ha fos en un:
- Desplegar és posar codi en producció: un acte tècnic, reversible, que pot passar vint vegades al dia.
- Llançar és fer que aquell codi sigui visible o efectiu per als usuaris: un acte de producte, amb data i amb màrqueting al darrere, que no ha de coincidir necessàriament amb l'anterior.
Quan tots dos coincideixen, apareixen les patologies que Reservalia coneix bé: branques de vida llarga esperant que la funcionalitat estigui "completa", desplegaments gegants carregats de canvis, i la impossibilitat d'apagar una cosa que va malament sense tornar a desplegar. Un feature flag (o toggle) és senzillament una condició al codi el valor de la qual es decideix en temps d'execució, fora de l'artefacte:
if (await flags.actiu('agenda_nou_calcul', { negociId })) {
return calcularForatsV2(horari, ocupats, duracioMin);
}
return calcularForats(horari, ocupats, duracioMin);L'artefacte reservalia/api:a3f9c21 conté les dues implementacions. Quina s'executa ja no depèn de quina imatge està desplegada, sinó d'un valor que es pot canviar en segons i sense pipeline. Això converteix "apagar la funcionalitat trencada" en una operació de deu segons en lloc d'un desplegament.
- Tipus de feature flag, vida esperada i propietari
No tots els flags són iguals, i tractar-los igual és la causa número u que un sistema de flags es podreixi. Reservalia adopta aquesta classificació:
| Tipus | Per a què serveix | Vida esperada | Propietari | Exemple a Reservalia |
|---|---|---|---|---|
| Release | Amagar codi incomplet o acabat de desplegar | Dies o setmanes; es retira sempre | El desenvolupador que el va crear | agenda_nou_calcul |
| Experiment | Comparar variants i mesurar efecte | El que duri la prova (setmanes) | Producte | experiment_horaris_suggerits |
| Operatiu / kill switch | Apagar una funcionalitat costosa o inestable en calent | Permanent | SRE (Nuria) | recordatoris_sms |
| Permisos / accés | Donar accés a un subconjunt de clients | Permanent, lligat al pla | Producte / Negoci | panel_metriques_beta |
Les dues columnes que més s'ignoren són les importants. La vida esperada distingeix el que s'ha d'esborrar del que s'hi queda: un flag de release que fa vuit mesos que és al codi és deute, un kill switch de vuit mesos és una eina. El propietari evita el flag orfe, aquell que ningú no gosa tocar perquè ningú no sap què passa si s'apaga.
- Una implementació mínima a
apps/api
apps/apiNo cal una plataforma comercial per començar. Reservalia desa els flags a la seva pròpia base de dades:
-- migracio: crear la taula de flags
CREATE TABLE flags (
clau TEXT PRIMARY KEY,
tipus TEXT NOT NULL CHECK (tipus IN ('release','experiment','operatiu','permisos')),
estat TEXT NOT NULL CHECK (estat IN ('off','percentatge','on')),
percentatge INT NOT NULL DEFAULT 0 CHECK (percentatge BETWEEN 0 AND 100),
negocis_inclosos BIGINT[] NOT NULL DEFAULT '{}', -- llista blanca explicita
propietari TEXT NOT NULL,
caduca_el DATE -- obligatoria en tipus 'release'
);El mòdul d'avaluació viu a apps/api/src/flags/index.ts:
import { createHash } from 'node:crypto';
type Context = { negociId: number };
const CACHE_MS = 30_000; // carregar() rellegeix la taula sencera com a molt un cop cada CACHE_MS
let cache: { el: number; valors: Map<string, Flag> } = { el: 0, valors: new Map() };
export async function actiu(clau: string, ctx: Context): Promise<boolean> {
const flag = (await carregar()).get(clau);
if (!flag) return false; // 1: absent = apagat
if (flag.negocis_inclosos.includes(ctx.negociId)) return true; // 2
if (flag.estat === 'on') return true;
if (flag.estat === 'off') return false;
return cubell(clau, ctx.negociId) < flag.percentatge; // 3
}
function cubell(clau: string, negociId: number): number { // 4
const h = createHash('sha256').update(`${clau}:${negociId}`).digest();
return h.readUInt32BE(0) % 100; // 5
}- Un flag desconegut retorna
false. El valor per defecte sempre és el comportament antic: si la taula no es pot llegir o algú ha escrit malament la clau, el sistema es comporta com abans del canvi, no com després. - La llista blanca permet activar la funcionalitat per a negocis concrets —els tres clients que van demanar la beta, o el negoci de proves intern— sense tocar el percentatge global.
- El repartiment per percentatge es calcula amb un hash, no amb un número aleatori. És la diferència entre un desplegament progressiu i un caos:
Math.random() < 0.1donaria un resultat diferent a cada petició i el mateix negoci veuria la funcionalitat aparèixer i desaparèixer. - En incloure la clau al hash, dos flags al 10 % no afecten els mateixos negocis; si el hash fos només del
negociId, sempre en sortirien els mateixos "escollits". - La memòria cau de 30 segons evita una consulta per petició. El preu és que un canvi triga fins a mig minut a propagar-se a totes les tasques: acceptable per a un kill switch, i cal saber-ho abans d'un incident.
- L'avaluació és per negoci, no per usuari, i és una decisió de domini: a Reservalia tots els empleats d'un mateix saló han de veure la mateixa agenda. Activar per usuari faria que dues recepcionistes veiessin forats diferents, que és exactament el tipus d'incident irreproduïble que ningú no vol.
- Fusionar codi incomplet a
main sense branques de vida llarga
main sense branques de vida llargaA la lliçó 02-07 va quedar establert que les branques de vida llarga són enemigues de la integració contínua: com més viuen, més divergeixen i més dolorós és el merge. Però l'equip tenia una objecció legítima: "no puc fusionar una funcionalitat a mig fer".
Els flags dissolen aquella objecció. En Diego pot fusionar calcularForatsV2 a main el primer dia, amb el flag agenda_nou_calcul en off: el codi viatja a producció a cada desplegament, es compila, passa el tsc --noEmit i les seves proves unitàries corren a la CI, però cap usuari no l'executa.
| Branca de vida llarga | Flag en off |
|
|---|---|---|
| Conflictes de merge | Creixen amb el temps | Cap: s'integra diàriament |
| La CI prova el codi | Només a la branca | A main, amb tota la resta |
| Activació | Requereix merge + desplegament | Canviar un valor |
| Desactivació | Revertir + desplegar | Canviar un valor |
| Cost | Zero al principi, alt al final | Complexitat al codi des del dia u |
I una regla pràctica: les proves han de cobrir totes dues branques del flag; a la CI s'executa el conjunt crític amb el flag forçat a on i a off, perquè un flag la branca activa del qual no es prova mai és codi mort que es despertarà en el pitjor moment.
- El deute de flags: per què caduquen i com es retiren
Cada flag de release afegeix una bifurcació al codi. Amb cinc flags convivint hi ha fins a 32 combinacions possibles de comportament, i ningú no prova 32 combinacions. Els flags no retirats produeixen símptomes concrets: codi il·legible, proves que depenen d'una configuració implícita, i el terror de tocar una cosa que "no se sap si encara es fa servir".
Reservalia estableix tres regles: tot flag de tipus release neix amb caduca_el obligatori, típicament a 30 dies vista; un job setmanal llista els flags caducats i obre una incidència assignada al seu propietari —no els esborra, perquè demanar una acció humana és diferent de trencar producció un dimarts—; i retirar el flag forma part de la tasca, no és una tasca futura: al tauler, una funcionalitat no està acabada fins que el seu flag ha desaparegut del codi.
-- Flags de release vencuts, amb el seu propietari
SELECT clau, propietari, caduca_el, estat FROM flags
WHERE tipus = 'release' AND caduca_el < CURRENT_DATE ORDER BY caduca_el;La retirada té un ordre que evita ensurts: primer es posa el flag al 100 % i es deixa uns dies, després s'elimina la condició del codi deixant només la branca nova, i només al final s'esborra la fila de la taula. A l'inrevés —esborrar la fila primer— el flag passa a avaluar-se com a false i la funcionalitat ja llançada desapareix de cop.
- Rollback d'artefacte davant de roll-forward
Quan alguna cosa va malament en producció hi ha dos camins, i triar per reflex és un error.
| Rollback d'artefacte | Roll-forward | |
|---|---|---|
| Què és | Tornar a desplegar el digest anterior conegut | Arreglar i desplegar una versió nova |
| Temps fins a la mitigació | Minuts: l'artefacte ja existeix i ja va estar sa | El que trigui l'arreglament + el pipeline complet |
| Risc | Baix: es torna a un estat conegut | Mitjà: codi nou escrit amb presses |
| Quan | La fallada és greu i la versió anterior estava bé | La fallada és lleu, o tornar enrere és impossible |
| Bloquejant | Migracions de base de dades incompatibles | Un pipeline lent |
Un equip madur no tria sempre el mateix. La regla que adopta Reservalia: si l'impacte és greu —usuaris que no poden reservar, errors 5xx, pèrdua de dades—, rollback sempre; l'arreglament es pensa després, amb calma i sense producció cremant. Si l'impacte és menor —un text mal traduït, una icona torta—, roll-forward, perquè un rollback també és un canvi i també pot fallar. Hi ha una condició que fa possible el rollback ràpid i que ja està pagada des de la 02-06: artefactes immutables identificats per digest. Tornar a b7e2d10 no vol dir reconstruir res, sinó apuntar el servei al digest que continua a ECR; reconstruir donaria una versió diferent amb les mateixes fonts, i això no és un rollback, és una loteria.
- El límit dur: la base de dades
El rollback d'artefacte és reversible. La migració de base de dades que l'acompanyava, no. Si la versió c1d4a55 incloïa una migració que va reanomenar la columna duracio a duracio_min, tornar a b7e2d10 deixa corrent un codi que consulta una columna que ja no existeix: el rollback del contenidor triga tres minuts, el de l'esquema pot no existir.
D'aquí surt una regla que governa tot el disseny: una migració ha de ser compatible amb la versió anterior del codi i amb la següent. S'aconsegueix amb el patró expandir-contraure que vam veure a la 03-04 aplicat a les dades: afegir la columna nova, escriure a totes dues durant un temps, migrar lectures, i només molt després eliminar la vella. Mentre es respecti, qualsevol desplegament és reversible. El detall complet —migracions al pipeline, ordre respecte del desplegament, migracions llargues i bloquejos— és la lliçó 04-06, Bases de Dades al Pipeline: Migracions Segures.
- El botó de tornada enrere:
rollback.yml
rollback.ymlLa Nuria escriu el workflow que faltava. El seu requisit de disseny és explícit: qualsevol persona de guàrdia, sense conèixer AWS, ha de poder revertir en menys de cinc minuts des del mòbil.
# .github/workflows/rollback.yml
name: Rollback
on:
workflow_dispatch: # 1
inputs:
entorn:
description: Entorn on revertir
type: choice
options: [dev, staging, prod]
required: true
sha: # SHA de 7 caracters, p. ex. b7e2d10
type: string
required: true
motiu: # queda registrat a la taula desplegaments
type: string
required: true
permissions: { id-token: write, contents: read }
concurrency: { group: desplegament-${{ inputs.entorn }}, cancel-in-progress: false } # 2
jobs:
revertir:
runs-on: ubuntu-22.04
environment: ${{ inputs.entorn }} # 3
steps:
- uses: actions/checkout@v4
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_DEPLOY }}
aws-region: eu-west-1
- name: Comprovar que l artefacte existeix a ECR
id: artefacte
run: | # 4
DIGEST=$(aws ecr describe-images --repository-name reservalia/api \
--image-ids imageTag=${{ inputs.sha }} \
--query 'imageDetails[0].imageDigest' --output text)
[ "$DIGEST" != "None" ] || { echo "::error::No existeix reservalia/api:${{ inputs.sha }}"; exit 1; }
echo "digest=$DIGEST" >> "$GITHUB_OUTPUT"
- name: Redesplegar el digest anterior i esperar estabilitat
run: | # 5
./infra/scripts/desplegar.sh reservalia-${{ inputs.entorn }} \
reservalia-api "${{ steps.artefacte.outputs.digest }}"
aws ecs wait services-stable --cluster reservalia-${{ inputs.entorn }} \
--services reservalia-api
- name: Smoke test contra /version
run: | # 6
BASE=https://api-${{ inputs.entorn }}.reservalia.com # a prod: api.reservalia.com
SHA=$(curl -fsS --retry 5 --retry-delay 5 "$BASE/version" | jq -r .commit)
[ "$SHA" = "${{ inputs.sha }}" ] || { echo "::error::Servint $SHA"; exit 1; }
- name: Registrar el rollback
run: ./infra/scripts/registrar-desplegament.sh --entorn "${{ inputs.entorn }}" \
--sha "${{ inputs.sha }}" --tipus rollback --motiu "${{ inputs.motiu }}"Les decisions que fan que això funcioni sota pressió són aquestes:
workflow_dispatchamb entrades tipades. Eltype: choiceelimina la possibilitat d'escriureproduccioen lloc deproda les tres de la matinada, i elmotiuobligatori garanteix que el registre serveixi per al post-mortem.- El mateix grup de
concurrencyquecd.yml. Un rollback i un desplegament normal no es poden solapar; el segon espera en lloc de trepitjar el primer. environment: ${{ inputs.entorn }}reutilitza les regles de protecció. Compte: siprodexigeix aprovació d'un revisor, el rollback també l'exigirà. Reservalia ho resol amb una llista d'aprovadors àmplia perquè sempre hi hagi algú disponible; bloquejar el rollback darrere d'una aprovació difícil d'aconseguir és pitjor que la fallada original.- Verificar que l'artefacte existeix abans de tocar res. Fallar de pressa amb un missatge clar és millor que deixar el servei a mig actualitzar. 5. Es reutilitza el mateix
desplegar.shdel desplegament normal, que parteix de la definició de tasca vigent i només canvia la imatge pel digest indicat: així el rollback no reverteix canvis de configuració legítims fets des d'aleshores, i el camí d'emergència fa servir codi ja provat diàriament. 6. El smoke test contra/versionconverteix "em sembla que ha tornat" en "està servintb7e2d10"; sense ell, el rollback és una esperança.
La Marta hi afegeix una pràctica que sembla trivial i no ho és: assajar el rollback una vegada al mes a staging, cronometrat. Un botó que ningú no ha premut mai no és un botó, és un adorn.
- Rollback automàtic disparat per mètriques
Durant un canary no cal esperar que algú miri un panell; el mateix pipeline pot decidir:
- name: Etapa 10 % amb vigilancia
run: |
./infra/scripts/pes-canary.sh 10
for i in $(seq 1 20); do sleep 30
./infra/scripts/comprovar-metriques.sh || { echo "::error::Canary degradat"; exit 1; }
done
- name: Avortar el canary si alguna cosa ha fallat
if: failure() # s executa nomes si un pas anterior ha fallat
run: ./infra/scripts/pes-canary.sh 0El patró clau és if: failure(): el pas d'avortament s'executa precisament quan alguna cosa ha anat malament, i retorna el pes del canary a zero sense esperar un humà. A Reservalia, comprovar-metriques.sh compara el grup canary amb l'estable en taxa de 5xx i latència p95 durant els deu minuts de vigilància. Dues cauteles: l'automatisme necessita llindars amb marge, o un pic de soroll revertirà desplegaments sans i l'equip acabarà desactivant-lo; i ha d'existir sempre el camí manual, perquè si l'automatisme falla, rollback.yml continua sent-hi.
- Recuperació d'incidents: mitigar primer, diagnosticar després
Un desplegament continu sa no presumeix de no fallar mai: presumeix de recuperar-se ràpid. L'ordre importa i és contraintuïtiu per a qui ve de la cultura del "cal entendre el problema abans de tocar res".
flowchart LR
A["Deteccio<br/>alerta o usuari"] --> B["Declarar incident<br/>i nomenar coordinador"]
B --> C["MITIGAR<br/>flag off · rollback · pes a 0"]
C --> D["Confirmar recuperacio<br/>metriques i /version"]
D --> E["Diagnosticar amb calma<br/>arreglar i desplegar"]
E --> G["Post-mortem sense culpables"]
Mitigar primer, diagnosticar després. Mentre s'investiga la causa, els usuaris continuen sense poder reservar. Apagar el flag o revertir l'artefacte atura el dany i retorna el temps que cal per pensar: la causa arrel continuarà allà d'aquí a dues hores; els clients enfadats, no necessàriament. Reservalia fixa tres rols durant un incident: qui coordina (decideix i no tecleja), qui opera (executa les accions) i qui comunica (avisa el suport i els negocis afectats). En un equip de tres persones els rols es poden solapar, però la coordinació no pot faltar: sense ella, dues persones executen mitigacions contradictòries alhora. I la comunicació té una regla simple: abans poc i aviat que tard i complet; un missatge als cinc minuts dient "estem investigant problemes en crear cites" val més que un informe perfecte al cap d'una hora.
- Post-mortem sense culpables
Un post-mortem blameless parteix d'una premissa: si una persona ha pogut trencar producció amb una acció raonable, el problema és el sistema que l'hi va permetre. Buscar culpables produeix ocultació, i l'ocultació produeix incidents que es repeteixen. Reservalia fa servir aquesta plantilla, a docs/postmortems/:
# Post-mortem: <titol curt i descriptiu>
- Data i durada: 2026-07-14, 09:12–09:34 (22 min)
- Impacte: ~180 negocis no van poder crear cites; 2.100 peticions amb error 500
- Deteccio: alerta de taxa d errors (4 min despres del desplegament de c1d4a55)
- Mitigacio: rollback.yml a b7e2d10 (6 min)
## Cronologia
09:08 es desplega c1d4a55 · 09:12 salta l alerta · 09:15 es declara incident …
## Que va passar i per que el sistema ho va permetre (sense noms propis)
Les proves d integracio no cobrien el cas d horari partit.
## Que va funcionar be
L alerta va disparar als 4 minuts; el rollback va trigar 6.
## Accions (amb propietari i data)
| Accio | Propietari | Data | Estat |
|---|---|---|---|
| Prova d integracio per a horaris partits | Diego | 2026-07-18 | fet |Dos apartats solen faltar i són els més útils: "què va funcionar bé", que evita desmuntar defenses que sí que van servir, i accions amb propietari i data, perquè un post-mortem sense accions assignades és un exercici literari. I una regla: les accions es fiquen al mateix tauler que la resta de la feina, o no es faran.
- De 68 minuts a menys de 10
El time to restore no és un número màgic: és la suma de tres trams, cadascun atacat amb una eina diferent.
| Tram | Abans | Després | Què ho aconsegueix |
|---|---|---|---|
| Detecció | ~25 min (ho avisava un client) | 3 min | Alertes sobre símptomes (lliçó 03-06) |
| Decisió | ~15 min ("revertim o arreglem?") | 2 min | Regla escrita: si l'impacte és greu, rollback |
| Execució | ~28 min (SSH, pujada manual, resar) | 4 min | rollback.yml i flags |
| Total | 68 min | 9 min |
Val la pena assenyalar quin tram s'endú la millora més gran: la detecció. Es pot tenir el millor botó de rollback del món i continuar trigant mitja hora si ningú no s'assabenta que hi ha un problema; aquell tram és justament el que la lliçó següent s'encarrega de tancar. I hi ha un cas encara millor: quan la fallada és darrere d'un flag, l'execució baixa a segons i el total ronda els cinc minuts.
Errors Comuns i Consells
Error 1: fer servir flags com a configuració permanent. Si flags acumula 60 claus de les quals 50 són de release i ningú no les retira, el codi es torna inauditable. Error 2: flags sense propietari ni caducitat, que ningú no gosa apagar anys després. Error 3: avaluar per atzar en lloc de per hash, amb la qual cosa el mateix negoci veu la funcionalitat aparèixer i desaparèixer entre peticions. Error 4: no provar la branca activa del flag; la CI dona verd i la funcionalitat esclata el dia que s'encén.
Error 5: un rollback que reconstrueix la imatge des del commit anterior. Això no és tornar a una versió coneguda, és construir-ne una de nova sota pressió: es desplega el digest, sempre. Error 6: no assajar mai el rollback, i descobrir que el rol IAM havia caducat justament durant l'incident.
Consell 1: fes que el valor per defecte de tot flag sigui el comportament antic, perquè una fallada de lectura sigui innòcua. Consell 2: registra cada canvi de flag amb autor i data, perquè en un incident la pregunta "què ha canviat?" inclou els flags, no només els desplegaments. Consell 3: documenta al README les tres ordres d'emergència —apagar flag, llançar rollback.yml, posar el canary a 0— on el de guàrdia les trobi en vint segons.
Exercicis
Exercici 1
Classifica aquests quatre flags de Reservalia per tipus, proposa vida esperada i propietari, i digues quin no s'ha de retirar mai: pagaments_stripe_v2 (nova passarel·la de pagament que substitueix l'antiga), recordatoris_sms (enviament d'SMS amb cost per missatge), panel_metriques_beta (panell disponible només per a clients del pla avançat) i experiment_horaris_suggerits (dues maneres d'ordenar els forats proposats).
Exercici 2
Són les 22:40. El desplegament de c1d4a55 fa vint minuts ha disparat la taxa d'errors de l'endpoint de creació de cites al 30 %. La versió anterior era b7e2d10. El canvi incloïa una migració que va afegir la columna recordatori_enviat_el. Descriu els passos exactes en ordre i justifica si tries rollback o roll-forward.
Exercici 3
Mateix escenari, però la migració va reanomenar duracio a duracio_min. Continua sent vàlid el teu pla? Descriu què faries i què s'hauria d'haver fet diferent setmanes abans perquè aquest escenari no existís.
Solucions
Solució 1. pagaments_stripe_v2 és de release: el seu objectiu és substituir la passarel·la antiga, així que té data de caducitat (unes setmanes, potser més pel que té de delicat el pagament), el seu propietari és qui el desenvolupa, i es retira quan el 100 % porti temps estable. experiment_horaris_suggerits és d'experiment: propietari de producte, viu el que duri el mesurament i acaba triant una variant. panel_metriques_beta és de permisos: el seu propietari és producte/negoci, és permanent i en realitat no és un flag temporal sinó una regla de drets d'accés lligada al pla contractat. recordatoris_sms és operatiu (kill switch) i és el que no es retira mai: com que els SMS costen diners i depenen d'un proveïdor extern, la Nuria necessita poder apagar-los en calent si el proveïdor es degrada o si el cost es dispara. Un matís útil: pagaments_stripe_v2, encara que sigui de release, convé mantenir-lo un temps prudencial després del 100 % perquè de facto actua com a kill switch de la passarel·la nova.
Solució 2. Rollback, sense dubtar-ho: l'impacte és greu (un terç de les creacions de cita falla), la versió anterior era sana i la migració és compatible —afegir una columna no molesta b7e2d10, que simplement la ignora—. Passos: (1) declarar l'incident i nomenar coordinador; (2) si el canvi és darrere d'un flag, apagar-lo, que són deu segons davant dels cinc minuts del rollback; (3) si no ho és, llançar rollback.yml amb entorn=prod, sha=b7e2d10 i el motiu; (4) confirmar amb el smoke test que /version retorna b7e2d10 i comprovar que la taxa d'errors baixa; (5) comunicar-ho al suport i als negocis afectats; (6) l'endemà, diagnosticar amb calma, arreglar, afegir la prova que faltava i desplegar cap endavant; (7) post-mortem amb accions assignades. La columna nova queda òrfena un temps, cosa que és inofensiva.
Solució 3. No, el pla ja no val: b7e2d10 consulta duracio, que ja no existeix, així que el rollback canviaria un error del 30 % per un error del 100 %. Les opcions reals són pitjors: roll-forward urgent amb un arreglament escrit sota pressió, o revertir també l'esquema —reanomenar la columna cap enrere— amb el risc de perdre escriptures fetes entremig. El que és raonable és mitigar per una altra via (apagar el flag si existeix, degradar la funcionalitat afectada) mentre es prepara l'arreglament cap endavant. El que s'hauria d'haver fet setmanes abans és aplicar expandir-contraure: desplegament 1, afegir duracio_min i escriure a totes dues columnes; desplegament 2, llegir de duracio_min; desplegament 3, setmanes després i amb tot estable, eliminar duracio. Amb aquella seqüència, cada desplegament individual és reversible i l'escenari de l'enunciat no arriba a produir-se. Aquest és exactament el territori de la lliçó 04-06.
Conclusió
Separar el desplegament del llançament canvia la naturalesa del risc. Els feature flags permeten a Reservalia fusionar codi incomplet a main sense branques de vida llarga, activar per negoci i amb repartiment estable per hash, i apagar en calent el que es trenqui —sempre que cada flag neixi amb tipus, propietari i data de caducitat, perquè el deute de flags és real—. El rollback d'artefacte per digest, materialitzat a rollback.yml amb workflow_dispatch, retorna producció a un estat conegut en minuts, i el rollback automàtic per mètriques ho fa sense esperar un humà durant un canary. Tot això amb un límit dur que convé tenir sempre present: si la migració de base de dades no és compatible, no hi ha tornada enrere que valgui.
Amb això, dos dels tres trams del time to restore de Reservalia estan resolts: la decisió, per una regla escrita, i l'execució, per un botó assajat. Queda el més gran, la detecció: avui Reservalia s'assabenta que producció va malament perquè truca un client. I hi ha una mancança bessona: sense senyal de producció, ni el canary automàtic ni el pressupost de desplegaments tenen sobre què decidir. La lliçó següent, Monitoratge i Retroalimentació, tanca el bucle amb els tres pilars de l'observabilitat, els quatre senyals d'or, els SLO amb pressupost d'error i l'alimentació automàtica de les mètriques DORA des del mateix pipeline.
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
