El contracte de la v1 de la Botiga Aroma està pràcticament tancat: recursos, mètodes, codis, representacions i col·leccions. I justament quan es publica un contracte comença el problema de debò: el negoci canvia i l'API ha de canviar amb ell, sense trencar res a ningú. Aroma Mòbil té versions instal·lades en telèfons que ningú no actualitzarà en mesos; RàpidEnviaments té una integració escrita fa un any que funciona i que ningú no vol tocar. Aquesta lliçó ensenya a distingir amb precisió quins canvis es poden fer sense avisar i quins no, compara les cinc estratègies de versionat que es fan servir al sector, justifica la de la Botiga Aroma i dissenya el cicle complet de depreciació, des de l'anunci fins a l'apagada.
Contingut
- Per què versionar és un darrer recurs
- Canvis retrocompatibles davant de trencadors
- Taula de canvis sobre l'API de la Botiga Aroma
- Estratègies de versionat
- Comparativa i decisió de la Botiga Aroma
- SemVer aplicat a APIs i els seus límits
- Convivència de versions
- Cicle de vida i depreciació
- Capçaleres
Deprecation,SunsetiWarning - Estratègies per no haver de versionar
- Per què versionar és un darrer recurs
Publicar una v2 sona a progrés. En realitat és una factura: dues bases de codi per mantenir, dues documentacions, dos conjunts de proves, dues superfícies de seguretat, i una migració que cal negociar amb cada consumidor. Empreses conegudes fa una dècada que mantenen la seva v1 perquè apagar-la va resultar impossible.
Per això la regla número u del versionat és: evita necessitar-lo. La majoria dels canvis es poden fer de manera retrocompatible si el contracte es va dissenyar amb tolerància a l'evolució (02-01) i si saps distingir amb precisió què trenca i què no. Això és exactament el que fa la secció següent.
- Canvis retrocompatibles davant de trencadors
La definició operativa, i l'única que serveix:
Un canvi és retrocompatible si un client escrit contra el contracte anterior, i que es comportava correctament, continua funcionant sense tocar-ne ni una línia.
Fixa't en el matís "que es comportava correctament": un client que es trenca perquè recorria els camps del JSON assumint que n'eren exactament cinc no és culpa teva, sempre que la documentació advertís que poden aparèixer camps nous. D'aquí que el principi de robustesa i el tolerant reader de 02-01 no siguin un consell tou: són la condició que fa possible evolucionar sense versionar.
2.1. Regla general
- Afegir és gairebé sempre segur (camps, endpoints, valors d'enumerat de sortida, paràmetres opcionals).
- Treure, reanomenar o restringir gairebé sempre trenca (camps, endpoints, valors admesos, codis d'estat).
2.2. L'asimetria entrada/sortida
És el que més confon, i val la pena pensar-hi a poc a poc: afegir un valor a un enumerat no és el mateix a la resposta que a la petició.
- A la sortida (respostes del servidor): afegir
estat: "retornat"és un canvi potencialment trencador per a clients que fan unswitchsense cas per defecte. Es considera acceptable només perquè la documentació avisa des del primer dia que els enumerats creixen. - A l'entrada (peticions del client): acceptar un valor nou a
torrefaccioés totalment segur, perquè ningú no l'estava enviant abans.
La simètrica, i per la mateixa raó invertida: relaxar una validació d'entrada és segur; endurir-la trenca. Si avui acceptes comentaris de 5.000 caràcters i demà els limites a 500, hi ha clients que funcionaven i deixen de funcionar.
- Taula de canvis sobre l'API de la Botiga Aroma
| Canvi | Trenca? | Per què |
|---|---|---|
Afegir el camp puntuacioMitjana a la resposta de cafè |
No | Els clients que l'ignoren continuen igual (tolerant reader) |
Afegir l'endpoint /v1/subscripcions |
No | Ningú no el cridava |
Afegir el filtre opcional ?disponible=true |
No | El comportament sense el paràmetre queda intacte |
Afegir l'enllaç retornar a _links d'una comanda |
No | Additiu, i ja vam avisar que els enllaços depenen de l'estat |
Acceptar un valor nou a torrefaccio d'entrada |
No | Ningú no enviava natural abans |
Afegir el valor retornat a estat de comanda |
Gairebé: documentat com a esperable | Trenca qui no va preveure valors nous; s'anuncia amb antelació |
Reanomenar preu a preuEuros |
Sí | Tot client que llegeixi preu rep undefined |
Eliminar el camp estoc de la resposta |
Sí | Desapareix una dada que s'estava fent servir |
Canviar estoc de nombre a cadena ("120") |
Sí | estoc > 0 deixa de comportar-se igual; "0" és cert en JavaScript |
Canviar preuEuros d'euros a cèntims (1450) |
Sí, i del pitjor tipus | No falla: mostra preus cent vegades més grans. Un canvi silenciós és pitjor que un de sorollós |
Fer obligatori el camp origen en crear un cafè |
Sí | Peticions que funcionaven ara donen 400 |
Limitar comentari de 5.000 a 500 caràcters |
Sí | Endurir una validació trenca qui estava al marge |
Eliminar el valor pendent_pagament d'estat |
Sí | Els clients el tenen als seus condicionals |
Canviar POST /comandes de 201 a 200 |
Sí | Els clients que comproven === 201 fallen; a més desapareix Location |
Canviar el codi d'error cafe_no_trobat a no_trobat_cafe |
Sí | El codi és contracte; es compara al codi del client (02-04) |
Canviar el text de missatge d'un error |
No | Es va documentar que missatge és per a humans i pot canviar |
Canviar l'ordre per defecte de /cafes de nom a -dataCreacio |
Sí | Encara que sembli innocu, canvia el que es veu a la pàgina 1 i estava documentat |
Reduir el limit màxim de 100 a 50 |
Sí | Peticions vàlides comencen a retornar 400 |
Augmentar el limit màxim de 100 a 200 |
No | Relaxar un límit és segur |
Canviar el format d'id de caf_001 a caf_01HQ8ZK… |
No | Es va documentar com a cadena opaca (02-02); només trenca qui el parsejava, que estava avisat |
Retornar null en un camp que mai no ho era |
Sí | El client fa cafe.origen.toUpperCase() i peta |
Migrar /cafes d'offset a cursor eliminant desplacament |
Sí | Un paràmetre publicat no es retira sense versió |
Afegir la capçalera Aroma-RateLimit-Restants |
No | Les capçaleres noves s'ignoren soles |
Corregir un 500 que ara retorna 400 |
No (es considera arranjament) | Estaves incomplint el teu propi contracte |
L'última fila assenyala una zona grisa real: arreglar un bug pot trencar qui depenia del bug. Es documenta al changelog, s'anuncia, i en general es considera un canvi no trencador. Però si el comportament erroni feia dos anys que hi era, potser s'ha convertit de facto en contracte: cal mirar-ho cas per cas.
- Estratègies de versionat
4.1. Versió a la ruta
És la més utilitzada del sector: Twitter/X, GitHub (durant anys), Stripe a la seva URL base, gairebé totes les APIs corporatives.
A favor: visible a simple vista; es prova amb un navegador o amb curl sense capçaleres; l'encaminament és trivial (un prefix); els logs i les mètriques separen versions sense esforç; els exemples de la documentació són autocontinguts.
En contra: els puristes de REST objecten que la URI hauria d'identificar el recurs, no el seu format, i que /v1/cafes i /v2/cafes són "el mateix cafè" amb dues URIs diferents; a més, versiona l'API sencera encara que només canviï un recurs, i els enllaços _links desats pels clients queden clavats a una versió.
4.2. Versió en query param
A favor: la URI base és única; és fàcil de provar; s'hi pot posar un valor per defecte.
En contra: es barreja amb els paràmetres de negoci (filtres, ordre, paginació), es perd amb facilitat en copiar URLs, complica la memòria cau i fa ambigu quina versió se serveix si el paràmetre falta.
4.3. Versió en capçalera personalitzada
A favor: les URIs queden netes i estables; permet versionar de manera granular.
En contra: invisible. No es pot enganxar un enllaç a un tiquet i esperar que reprodueixi el problema; provar-ho en un navegador és impossible sense eines; les memòries cau necessiten Vary: Aroma-Versio i moltes passarel·les ho ignoren; i cal decidir què passa si no s'envia.
4.4. Versió al media type
És l'opció més correcta des del punt de vista de REST: la versió pertany a la representació, i la representació es negocia amb Accept (02-05). GitHub la va fer servir durant anys (application/vnd.github.v3+json).
A favor: teòricament impecable; fa servir un mecanisme estàndard d'HTTP; permet versionar recurs a recurs.
En contra: la més difícil de fer servir. Ningú no recorda la cadena de memòria; curl requereix una capçalera explícita; moltes eines i clients generats no la gestionen bé; i igual que la capçalera personalitzada, és invisible a la URL.
4.5. Versionat per data
És l'estil de Stripe: cada compte queda ancorat a la versió vigent el dia que es va integrar, i el servidor aplica transformacions encadenades per adaptar la resposta actual a la forma que esperava aquella data.
A favor: no hi ha salts traumàtics de v1 a v2; els canvis s'introdueixen de manera contínua; cada consumidor migra quan vol; és el que millor escala en APIs públiques grans.
En contra: complexitat alta. Exigeix mantenir una cadena de transformacions ben provada i una disciplina d'enginyeria considerable. És un patró excel·lent per a una empresa el producte de la qual és l'API, i desproporcionat per a gairebé totes les altres.
- Comparativa i decisió de la Botiga Aroma
| Criteri | Ruta | Query | Capçalera | Media type | Data |
|---|---|---|---|---|---|
| Visibilitat | Alta | Alta | Baixa | Baixa | Baixa |
Facilitat de prova (curl, navegador) |
Molt alta | Alta | Baixa | Molt baixa | Baixa |
| Puresa REST | Baixa | Baixa | Mitjana | Alta | Mitjana |
| Granularitat (per recurs) | Baixa | Baixa | Alta | Alta | Alta |
| Facilitat d'encaminament i desplegament | Molt alta | Mitjana | Mitjana | Baixa | Baixa |
| Memòria cau i intermediaris | Senzilla | Mitjana | Requereix Vary |
Requereix Vary |
Requereix Vary |
| Complexitat d'implementació | Baixa | Baixa | Mitjana | Mitjana | Alta |
| Migració progressiva | Baixa | Baixa | Mitjana | Mitjana | Molt alta |
| Qui la fa servir | Twitter/X, la majoria | APIs senzilles | Azure, algunes | GitHub (històric) | Stripe |
Decisió de la Botiga Aroma: versió a la ruta (/v1).
Les raons, en ordre de pes:
- Visibilitat i suport. Quan RàpidEnviaments obri una incidència i enganxi una URL, sabrem exactament contra què està parlant. Amb capçaleres, la meitat dels tiquets comencen amb "quina versió feies servir?".
- Cost d'implementació i operació. Un prefix de ruta s'encamina, es desplega, es mesura i s'apaga amb eines estàndard. Amb quatre consumidors i un equip petit, la puresa teòrica no compensa.
- Docència i documentació. Tots els exemples
curlde la documentació funcionen copiats i enganxats, sense capçaleres ocultes. - Coherència amb el que ja s'ha decidit. La base URL amb
/v1està fixada des del mòdul 1 i publicada als quatre consumidors.
I les decisions associades, que també són contracte:
- Només es versiona el número major:
/v1,/v2. Mai/v1.2. Els canvis menors són retrocompatibles per definició i no necessiten una URL nova. - La versió és obligatòria a la ruta. No existeix
https://api.botigaaroma.example/cafessense versió que "apunti a l'última": un client que no tria versió acaba trencat el dia que l'última canvia. - Tots els recursos comparteixen versió. Puja tota l'API alhora, encara que només canviïn les comandes. Simplifica el raonament a canvi d'una mica de granularitat.
- SemVer aplicat a APIs i els seus límits
SemVer (versionat semàntic) defineix MAJOR.MENOR.PEDAÇ:
| Component | Quan puja | Exemple a la Botiga Aroma |
|---|---|---|
| MAJOR | Canvi trencador | Reanomenar preu a preuEuros |
| MENOR | Funcionalitat nova, retrocompatible | Afegir /v1/subscripcions |
| PEDAÇ | Correcció sense canvi de contracte | Arreglar un càlcul d'IVA |
Aplicat a una API HTTP, SemVer té tres límits que convé tenir clars:
- Només el número major apareix a la URL. A un consumidor tant li fa si està fent servir la
1.4.2o la1.7.0: el contracte que veu és el mateix. Menor i pedaç viuen al changelog, no a la ruta. - No hi ha cap "instal·lació" que es pugui fixar. Amb una biblioteca, el consumidor decideix quan actualitzar; amb una API allotjada, el servidor actualitza per a tothom alhora. Per això els canvis menors han de ser rigorosament retrocompatibles: no hi ha marxa enrere per al client.
- La frontera major/menor es negocia. Afegir un valor d'enumerat a la sortida (secció 2.2) és, tècnicament, potencialment trencador; declarar-ho MAJOR obligaria a publicar una
v2cada trimestre. Es documenta com a esperable i es tracta com a MENOR amb anunci previ. Escriu aquesta política a la guia d'estil, perquè és la decisió que més discussions evita.
La Botiga Aroma manté, per tant, dues numeracions: la versió de la URL (v1) per al contracte, i la versió semàntica interna (1.7.0) al changelog i al camp info.version d'OpenAPI (02-08).
- Convivència de versions
Quan arriba la v2, totes dues conviuen un temps. Les preguntes pràctiques:
Quantes versions cal mantenir?
Dues com a màxim: l'actual i l'anterior en depreciació. Tres versions vives és senyal que la migració anterior no va acabar mai, i el cost creix més que linealment: cada correcció de seguretat i cada canvi de negoci s'ha d'aplicar a totes.
Quant costa realment?
| Cost | Detall |
|---|---|
| Codi | Rutes, transformacions i, de vegades, lògica de negoci duplicada |
| Proves | Tota la bateria, dues vegades (03-08) |
| Documentació | Dues referències completes i coherents |
| Suport | El doble de casuística a cada incidència |
| Seguretat | Cada pedaç, aplicat i verificat dues vegades |
| Dades | La v1 pot necessitar camps que la v2 ja no fa servir |
Com s'encaminen?
El patró habitual, i el que farà servir la Botiga Aroma al mòdul 3: una sola aplicació amb dues capes de presentació sobre una lògica de negoci compartida.
graph TD
C1["Aroma Mòbil 3.x"] --> R{"Encaminament<br/>per prefix"}
C2["SPA de la botiga"] --> R
C3["RàpidEnviaments"] --> R
R -->|"/v1/*"| V1["Capa v1<br/><i>transforma al contracte antic</i>"]
R -->|"/v2/*"| V2["Capa v2<br/><i>contracte actual</i>"]
V1 --> N["Lògica de negoci<br/>i dades<br/><b>compartides</b>"]
V2 --> N
La clau és no duplicar la lògica de negoci. La v1 s'implementa com una capa d'adaptació sobre el model actual: reanomena camps, retalla el que no existia, calcula el que es va eliminar. Duplicar el servei sencer garanteix que les dues versions divergeixin en comportament i que apareguin bugs que només passen en una.
Quan la transformació deixa de ser possible —perquè el model de dades va canviar de debò— és el senyal que la v1 s'ha d'apagar, no que calgui duplicar el sistema.
- Cicle de vida i depreciació
Tota versió recorre quatre fases:
graph LR
A["<b>Vigent</b><br/>versió recomanada"] --> B["<b>Deprecada</b><br/>funciona, però avisa"]
B --> C["<b>Sunset anunciat</b><br/>data d'apagada fixada"]
C --> D["<b>Apagada</b><br/>410 Gone"]
El calendari de la Botiga Aroma, escrit a la documentació abans de publicar la v1 —perquè les condicions de retirada s'anuncien al principi, no quan ja molesten—:
| Fita | Termini | Què passa |
|---|---|---|
Publicació de la v2 |
Dia 0 | La v1 continua vigent i suportada |
Depreciació de la v1 |
Dia 0 | Capçalera Deprecation a totes les respostes v1; changelog i correu als consumidors |
| Recordatoris | Mesos 3, 6, 9, 11 | Correu als consumidors que continuen cridant-la, amb les seves xifres d'ús |
| Només lectura (opcional) | Mes 11 | Les escriptures a v1 retornen 410; les lectures continuen |
| Apagada | Mes 12 | Tota la v1 respon 410 Gone amb enllaç a la guia de migració |
Mínim de 12 mesos per a consumidors externs com RàpidEnviaments. Per a clients propis (SPA, panell) el termini es pot escurçar perquè en controlem el desplegament, però no per a Aroma Mòbil: hi ha usuaris que no actualitzen l'app en un any, i aquí hi ha el consumidor que de debò marca el calendari.
Comunicació
Una depreciació que només viu a les capçaleres HTTP és una depreciació que ningú no llegeix. El paquet complet:
- Capçaleres a cada resposta (secció 9) — per al programari.
- Changelog amb data, motiu i guia de migració camp a camp — per al desenvolupador que investiga.
- Correu directe als responsables tècnics de cada consumidor identificat — per a l'humà que decideix.
- Avís al portal de desenvolupador (05-06).
- Recordatoris segmentats: només a qui continua fent servir la versió antiga, amb les seves dades concretes d'ús. Un correu genèric s'ignora; "hem registrat 12.400 crides vostres a
/v1aquest mes" no.
Mètriques: saber qui hi continua sent
No s'apaga res sense dades. Cal mesurar, per versió i per consumidor:
| Mètrica | Per a què |
|---|---|
| Peticions per versió i dia | Veure la corba de migració i decidir si el termini és realista |
| Peticions per versió i client d'API | Saber a qui cal trucar per telèfon |
Endpoints v1 més utilitzats |
Prioritzar la guia de migració pel que de debò es fa servir |
| Darrer accés per client | Detectar integracions zombis que potser ja no importen |
Errors a v2 després de migrar |
Descobrir que la migració va malament abans que es queixi ningú |
Això exigeix identificar cada consumidor, cosa que s'aconsegueix amb la clau o el token d'API de cadascun (04-03) i amb l'observabilitat de 04-07. Sense identificació de consumidors no hi ha depreciació possible: només apagades a cegues.
L'apagada
En arribar la data, la v1 respon:
HTTP/1.1 410 Gone
Content-Type: application/json
Link: <https://docs.botigaaroma.example/migracio-v1-v2>; rel="deprecation"
{
"error": {
"codi": "versio_api_retirada",
"missatge": "La versió v1 de l'API es va retirar el 2027-03-14. Migra a /v2.",
"detalls": [
{ "guiaMigracio": "https://docs.botigaaroma.example/migracio-v1-v2" }
]
}
}410 Gone i no 404: el recurs va existir i s'ha eliminat deliberadament (02-04). I no es redirigeix v1 a v2 amb un 301: els contractes són diferents, així que el client rebria una resposta amb una altra forma i fallaria de manera confusa. És millor un error clar que un èxit enganyós.
- Capçaleres
Deprecation, Sunset i Warning
Deprecation, Sunset i WarningL'estàndard permet anunciar la retirada dins del mateix protocol, de manera que el programari se'n pugui assabentar sense llegir cap correu.
HTTP/1.1 200 OK
Content-Type: application/json
Deprecation: @1773484200
Sunset: Sun, 14 Mar 2027 00:00:00 GMT
Link: <https://api.botigaaroma.example/v2/cafes>; rel="successor-version",
<https://docs.botigaaroma.example/migracio-v1-v2>; rel="deprecation"
{ "dades": [ ], "total": 137 }| Capçalera | Estàndard | Què diu |
|---|---|---|
Deprecation |
RFC 9745 | Que el recurs està deprecat, i des de quan (data amb @ i segons epoch, o true) |
Sunset |
RFC 8594 | Data exacta a partir de la qual deixarà de respondre, en format de data HTTP |
Link amb rel="successor-version" |
RFC 8288 | On és el substitut |
Link amb rel="deprecation" |
RFC 9745 | On és l'explicació i la guia de migració |
Warning |
RFC 7234, obsoleta | Avisos llegibles; retirada a la RFC 9111: no la facis servir en dissenys nous |
Un detall important i que s'oblida: aquestes capçaleres es poden fer servir sense publicar cap versió nova, sobre un recurs o un camp concret. Si la Botiga Aroma ha de retirar /v1/clients/{id}/preferencies substituint-lo per una altra cosa, aquest endpoint concret pot portar Deprecation i Sunset mentre la resta de la v1 continua ben sana.
I una recomanació pràctica per als consumidors, que convé incloure a la documentació: registreu un avís als vostres logs quan arribin aquestes capçaleres. És la manera barata d'assabentar-se d'una depreciació sense dependre que algú llegeixi el correu adequat.
- Estratègies per no haver de versionar
Tornem al punt de partida: la millor versió nova és la que no cal. Cinc tècniques concretes.
10.1. Camps opcionals i additius
Afegir en lloc de canviar. Quan calgui substituir un camp, tots dos conviuen durant la transició:
preu queda documentat com a deprecat, amb data de retirada, i desapareix a la v2. Costa duplicar una dada durant uns mesos; estalvia una versió sencera.
10.2. Tolerant reader
És responsabilitat del consumidor, i cal documentar-la i repetir-la:
// ✗ Fràgil: es trenca amb qualsevol camp nou o qualsevol valor nou
const { id, nom, preuEuros, estoc, torrefaccio } = cafe;
switch (comanda.estat) {
case "pendent_pagament": mostrarBotoPagar(); break;
case "pagat": mostrarFactura(); break;
case "enviat": mostrarSeguiment(); break;
}
// ✓ Tolerant: ignora allò desconegut i té cas per defecte
const nom = cafe.nom ?? "Sense nom";
switch (comanda.estat) {
case "pendent_pagament": mostrarBotoPagar(); break;
case "pagat": mostrarFactura(); break;
case "enviat": mostrarSeguiment(); break;
default: mostrarEstatGeneric(comanda.estat);
}La segona versió sobreviu al dia en què aparegui estat: "retornat". Un client tolerant és el que converteix "afegir" en una operació segura, i per això la Botiga Aroma ho documenta com a requisit d'integració, no com a consell.
10.3. Feature flags i desplegament progressiu
Un comportament nou s'activa primer per a un consumidor concret, es mesura i es generalitza. Permet validar un canvi dubtós amb la SPA (que controlem) abans d'exposar-lo a RàpidEnviaments. Compte: una bandera que es queda per sempre és una versió encoberta; tota bandera necessita data de retirada.
10.4. Expansió i selecció de camps
Ja dissenyades a 02-05: expandir i camps absorbeixen bona part de les peticions de canvi ("necessitem les dades del client dins de la comanda") sense tocar el contracte, perquè el mecanisme genèric ja hi estava previst.
10.5. Recursos nous en lloc de recursos canviats
Si /cafes ha de canviar de manera radical, de vegades la resposta correcta no és la v2 completa, sinó un recurs nou amb nom propi (/cataleg, /productes) que conviu amb l'antic, ja deprecat. Es paga amb dos noms per a conceptes semblants, i es cobra en no versionar l'API sencera per un sol recurs.
Errors Comuns i Consells
- Versionar per costum. Publicar una
v2perquè "toca" duplica el cost sense donar valor. Versiona només quan un canvi trencador sigui inevitable. - Creure que afegir un camp no trenca mai. És cert per a clients tolerants; amb validació estricta d'esquema al client, trenca. Per això cal documentar que l'API pot afegir camps.
- Canviar el significat d'un camp sense canviar-ne el nom. El pitjor canvi possible: no falla, menteix.
preuEurospassant a cèntims multiplica els preus per cent silenciosament. - No versionar el contracte d'errors. Els
codid'error són contracte tant com els camps: reanomenar-los trenca clients (02-04). - Mantenir tres o quatre versions vives. És un símptoma, no una virtut: significa que cap migració no es va completar.
- Apagar sense dades ni avís. Sense mètriques per consumidor i sense termini anunciat, l'apagada és una incidència greu amb el teu soci.
- Redirigir
v1av2amb301. Els contractes són diferents: el client rebrà200amb una forma que no espera i fallarà de manera incomprensible. - Consell: escriu el changelog des del primer dia. És l'artefacte més barat i el que més agraeixen els consumidors (02-08).
- Consell: aplica el "test del client congelat". Davant de cada canvi, pregunta't: continuaria funcionant la versió d'Aroma Mòbil que es va instal·lar fa un any? Si la resposta és no, és trencador.
Exercicis
Exercici 1: classificar canvis
Per a cada canvi proposat sobre la v1 de la Botiga Aroma, indica si és retrocompatible o trencador i justifica-ho. Si és trencador, proposa una alternativa que no ho sigui.
- Afegir
puntuacioMitjanainombreRessenyesa la representació de cafè. - Reanomenar
notesTastanotesDeTast. - Afegir l'estat
retornata les comandes. - Deixar de retornar
emaila la representació de client per privacitat. - Acceptar
PATCHambapplication/json-patch+json, a més de Merge Patch. - Canviar
totalEurosde29.00a"29.00"(cadena) per evitar problemes de coma flotant. - Fer obligatòria
Idempotency-KeyaPOST /cafes/{id}/ressenyes. - Abaixar el
limitmàxim de 100 a 50 per problemes de rendiment.
Exercici 2: dissenyar una migració
La Botiga Aroma necessita suportar diverses divises. La forma actual és:
I la desitjada:
Dissenya la migració completa: és trencadora?, es pot evitar la v2?, què es publica i quan?, quines capçaleres s'envien i què es comunica a cadascun dels quatre consumidors?
Exercici 3: planificar la retirada de la v1
Han passat sis mesos des que es va publicar la v2 i aquestes són les dades d'ús de la v1:
| Consumidor | Peticions/mes a v1 |
Peticions/mes a v2 |
Darrer accés |
|---|---|---|---|
| SPA de la botiga web | 0 | 4.200.000 | — |
| Aroma Mòbil | 890.000 | 3.100.000 | Avui |
| Panell intern | 12.000 | 45.000 | Avui |
| RàpidEnviaments | 61.000 | 0 | Avui |
Client desconegut api_key_7f2 |
340 | 0 | Fa 4 mesos |
Decideix si es pot apagar la v1 al mes 12 i elabora el pla d'acció per a cada consumidor.
Solucions
Solució 1
| # | Veredicte | Justificació i alternativa |
|---|---|---|
| 1 | Retrocompatible | Camps additius; els clients que no els coneixen els ignoren |
| 2 | Trencador | Tot client que llegeixi notesTast rep undefined. Alternativa: retornar els dos camps, documentar notesTast com a deprecat amb Sunset, i eliminar-lo a la v2 |
| 3 | Gairebé trencador, acceptat | Trenca qui no tingui cas per defecte, però la documentació adverteix que els enumerats creixen. S'anuncia al changelog amb antelació i es comunica als consumidors |
| 4 | Trencador | Desapareix una dada en ús. Alternativa: deixar de retornar-la només als consumidors sense permís per a dades personals (04-03), que és un canvi d'autorització, no de contracte; i per a la resta, deprecar-la amb termini |
| 5 | Retrocompatible | Ampliar els formats acceptats a l'entrada és relaxar, no restringir: ningú no enviava JSON Patch abans |
| 6 | Trencador | Canvia el tipus: totalEuros * 2 deixa de funcionar i les comparacions numèriques fallen. Alternativa: afegir totalEurosText com a camp nou i migrar a poc a poc, o deixar-ho per a la v2 |
| 7 | Trencador | Peticions que funcionaven comencen a donar 400. Alternativa: acceptar-la com a opcional, avisar durant mesos que serà obligatòria, mesurar quants clients ja l'envien i fer-la obligatòria a la v2 |
| 8 | Trencador | Peticions vàlides passen a 400. Alternatives: optimitzar la consulta; mantenir 100 i limitar per consumidor mitjançant rate limiting (04-04); o anunciar la reducció amb un termini llarg i mesurar quants en fan servir més de 50 |
Solució 2
És trencadora? Sí, sense matisos: preuEuros desapareixeria i canviaria de tipus (nombre → objecte). Qualsevol client que mostri preus es trenca, i en el pitjor dels casos mostra [object Object].
Es pot evitar la v2? Sí, amb la tècnica de convivència de camps, i aquesta és la resposta correcta:
Fase 1 (mes 0). S'hi afegeix preu sense treure res:
Canvi retrocompatible: els clients antics continuen llegint preuEuros; els nous fan servir preu. Mentre només hi hagi euros, els dos camps coexisteixen sense ambigüitat. Es publica al changelog, es documenta preuEuros com a deprecat i s'explica l'equivalència.
Fase 2 (mesos 0-12). Respostes amb capçaleres a nivell de camp i seguiment d'ús:
Deprecation: @1773484200
Sunset: Sun, 14 Mar 2027 00:00:00 GMT
Link: <https://docs.botigaaroma.example/migracio-preu>; rel="deprecation"Es mesura quins consumidors continuen llegint preuEuros —cosa que a la pràctica exigeix preguntar-los-ho, perquè el servidor no veu quins camps fa servir el client: aquí és on camps= de 02-05 resulta útil com a indici.
Fase 3. Quan aparegui la primera divisa diferent de l'euro, preuEuros deixa de ser representable i aleshores sí neix la v2, que elimina el camp antic. És a dir: la v2 s'ajorna fins que el negoci la justifiqui, no la provoca un canvi de forma.
Comunicació per consumidor:
| Consumidor | Acció |
|---|---|
| SPA de la botiga | Migració immediata: la controlem i es desplega en dies |
| Aroma Mòbil | Migrar a la versió següent de l'app; cal assumir 12 mesos de convivència per les instal·lacions antigues |
| Panell intern | Migració immediata |
| RàpidEnviaments | Correu formal amb la guia de migració i la data; no consumeix preus, així que probablement no l'afecti, però se l'informa igualment |
Solució 3
Veredicte: no es pot apagar al mes 12 sense feina prèvia. Hi ha dos bloqueigs seriosos i un de menor.
| Consumidor | Diagnòstic | Pla d'acció |
|---|---|---|
| SPA | Migrada al 100 % | Res |
| Aroma Mòbil | 890.000 crides/mes: hi ha versions antigues instal·lades en telèfons. No n'hi ha prou de publicar una versió nova de l'app | Forçar l'actualització des de la mateixa app (avís bloquejant), mesurar la distribució de versions instal·lades i no apagar fins que la corba baixi del llindar acordat. És el bloqueig principal |
| Panell intern | 12.000 crides/mes: queden pantalles sense migrar | Auditoria d'endpoints v1 utilitzats i migració; és intern, així que és qüestió de planificar la feina. Termini: mes 8 |
| RàpidEnviaments | 0 crides a v2: no ha començat a migrar. El bloqueig més perillós, perquè és extern i no en controlem el calendari |
Contacte directe immediat amb el seu equip tècnic, guia de migració específica, entorn de proves i data compromesa per escrit. Si no pot complir el mes 12, es negocia una extensió acotada només per a la seva clau d'API, amb data nova i signada |
api_key_7f2 |
340 crides, sense activitat en 4 mesos: integració zombi o script oblidat | Intentar identificar-ne el responsable per les dades d'alta; si no hi ha resposta en 30 dies, avisar de la retirada i apagar a la data prevista. No ha de condicionar el pla |
Pla revisat: mantenir la data de depreciació, però fixar l'apagada al mes 12 condicionada a dues fites mesurables: (1) que Aroma Mòbil v1 baixi del 2 % del trànsit total, i (2) que RàpidEnviaments confirmi per escrit la seva migració. Entre els mesos 9 i 12, recordatoris mensuals amb xifres concretes; al mes 11, v1 en només lectura per forçar la detecció d'integracions oblidades; i un assaig d'apagada (brownout) d'una hora al mes 10, anunciat amb antelació, que és la tècnica més eficaç perquè apareguin els consumidors que ningú no sabia que existien.
Conclusió
Versionar és car, així que l'objectiu real és necessitar-ho el mínim possible. Ara saps distingir amb precisió un canvi retrocompatible d'un de trencador —afegir és segur; treure, reanomenar, restringir i canviar tipus no ho és, amb l'asimetria clau entre entrada i sortida— i coneixes la pitjor categoria de totes: el canvi silenciós que no falla, sinó que menteix. Has comparat les cinc estratègies de versionat i saps per què la Botiga Aroma versiona a la ruta amb /v1, prioritzant visibilitat i cost d'operació per damunt de la puresa REST del media type. I tens el cicle de vida complet: convivència de dues versions com a màxim sobre una lògica de negoci compartida, dotze mesos de termini per als consumidors externs, capçaleres Deprecation i Sunset amb Link al successor i a la guia de migració, mètriques per consumidor per saber a qui trucar, i una apagada amb 410 Gone en lloc d'una redirecció enganyosa.
Amb això, el contracte de la v1 està complet i té a més una política d'evolució. Falta la peça que el converteix en una cosa utilitzable per altres: explicar-lo. A la lliçó següent i darrera del mòdul, 02-08 Documentació d'APIs, veurem per què la documentació és part del producte, quins tipus de document serveixen a quin lector, què ha d'incloure la referència de cada endpoint, i què canvia quan el contracte s'escriu en un format llegible per màquines com OpenAPI. Tancarem fent balanç del contracte complet de la Botiga Aroma i preparant el salt al mòdul 3, on per fi s'implementa.
Curs de REST API: Principis de Disseny i Desenvolupament d'APIs RESTful
Mòdul 1: Introducció a les APIs RESTful
- Què és una API?
- Història i evolució de les APIs
- Fonaments d'HTTP per a APIs
- Principis bàsics de REST
- Model de maduresa de Richardson i HATEOAS
- REST vs. SOAP
- REST davant de GraphQL, gRPC i webhooks
Mòdul 2: Disseny d'APIs RESTful
- Principis de disseny d'APIs RESTful
- Recursos i URIs
- Mètodes HTTP
- Codis d'estat HTTP
- Representacions, capçaleres i negociació de contingut
- Filtratge, ordenació, paginació i cerca
- Versionat d'APIs
- Documentació d'APIs
Mòdul 3: Desenvolupament d'APIs RESTful
- Configuració de l'entorn de desenvolupament
- Creació d'un servidor bàsic
- Gestió de peticions i respostes
- Validació de dades d'entrada
- Persistència i capa d'accés a dades
- Autenticació i autorització
- Gestió d'errors
- Proves i validació
Mòdul 4: Bones Pràctiques i Seguretat
- Bones pràctiques en el disseny d'APIs
- Seguretat en APIs RESTful
- OAuth 2.0 i OpenID Connect a la pràctica
- Rate limiting i throttling
- CORS i polítiques de seguretat
- Memòria cau HTTP i rendiment
- Observabilitat: logs, mètriques i traces
Mòdul 5: Eines i Frameworks
- Postman per a proves d'APIs
- Swagger i OpenAPI per a documentació
- Frameworks populars per a APIs RESTful
- Contractes, mocks i proves automatitzades d'API
- Integració contínua i desplegament
- API gateways i portals de desenvolupador
