Modificări de API

Cele mai bune practici de versionare API, pentru apelanți

8 min de citit

Versionarea API-ului e practica de a menține un contract vechi funcțional după ce l-ați schimbat, ca apelanții să poată avansa după propriul lor program, nu al vostru. Acea propoziție conține cele două decizii care contează: ce contează ca schimbare a contractului, și cât timp continuă să funcționeze cel vechi. Unde trăiește numărul versiunii, despre ce e majoritatea dezbaterilor de versionare, e cel mai puțin important din cele trei și cel mai ușor de făcut corect.

Când ar trebui versionat un API?

Versionați un API doar când o schimbare ar rupe un apelant corect. Schimbările aditive, câmpuri noi, endpoint-uri noi, parametri opționali noi, nu au nevoie de versiune; apelanții scriși pentru contractul vechi continuă să funcționeze, iar noua capacitate e pur și simplu acolo. O schimbare care rupe compatibilitatea are nevoie de una, pentru că alternativa e ca un apelant să afle dintr-o eroare. Versionarea fiecărei lansări, inclusiv cele aditive, îi învață pe apelanți că versiunile sunt zgomot, și încetează să citească notificările care contează.

Testul practic e cel din articolul despre schimbările care rup compatibilitatea: dacă un apelant care s-a bazat doar pe comportamentul documentat trebuie să schimbe ceva ca să continue să funcționeze, schimbarea are nevoie de o versiune. Dacă nu, lansați-o sub versiunea curentă și scrieți o intrare de changelog.

Ce schemă de versionare API ar trebui folosită?

Folosiți schema pe care apelanții voștri o pot vedea și seta cel mai ușor, ceea ce pentru majoritatea API-urilor publice e o versiune în calea URL-ului sau un header de versiune datat. Cele patru scheme comune diferă mai puțin în capacitate decât în ce cer de la apelant, și asta e baza corectă pentru a alege.

SchemăExempluCe trebuie să facă apelantulCine o folosește
Cale URL/v2/invoicesSă schimbe URL-ul la migrareMajoritatea API-urilor REST publice
Header de versiuneX-GitHub-Api-Version: 2022-11-28Să trimită un header, sau să accepte implicitulGitHub
Versiune de cont datatăStripe-Version: 2026-08-26Să fixeze o dată per cerere sau per contStripe
Parametru de query/invoices?version=2Să adauge un parametruAPI-uri mai vechi; rar ales acum
Tip de mediaAccept: application/vnd.example.v2+jsonSă negocieze tipuri de conținutPuriști; puțini apelanți se descurcă

Calea URL e cea mai vizibilă și cea mai puțin flexibilă. Fiecare apelant poate vedea în ce versiune e citind o linie de log, iar un salt de versiune e un caută-și-înlocuiește. Costul: întreaga suprafață se mișcă deodată, nu puteți schimba contractul unui singur endpoint fără să bateți o versiune nouă pentru toate, deci versiunile de cale tind să fie rare și mari.

Header-ul de versiune menține URL-urile stabile și lasă serverul să aleagă un implicit pentru apelanții care nu trimit nimic, așa cum funcționează versionarea API REST a GitHub: o versiune numită după dată în X-GitHub-Api-Version, cu cea mai veche versiune suportată ca implicit ca apelanții nefixați să nu se rupă. Costul: versiunea e invizibilă într-un URL și ușor de uitat într-un client nou.

Versiunea de cont datată e schema de header plus o adăugire: versiunea e stocată contra contului, deci fiecare cerere o primește fără să trimită nimic. Versionarea API a Stripe fixează fiecare cont la versiunea cu care a fost creat și lasă o cerere să suprascrie asta cu Stripe-Version. E schema cea mai prietenoasă pentru apelant și cea mai multă muncă de operat, pentru că serverul trebuie să traducă între fiecare versiune suportată și cea curentă.

Parametrul de query și tipul de media funcționează ambele și eșuează ambele testul de vizibilitate în moduri diferite: un parametru de query se pierde ușor la construirea unui URL, iar o versiune de tip de media e invizibilă pentru aproape orice unealtă cu care ar depana un apelant. Schema datată a Stripe e cel mai cunoscut exemplu al abordării cu dată, iar cum versionează Stripe API-ul o parcurge pas cu pas.

Cum se face versionarea API în practică?

În practică o versiune e un set numit de comportamente, iar serverul mapează fiecare cerere la unul dintre ele. Pașii sunt aceiași indiferent ce schemă poartă numele.

  1. Numiți versiunile după dată sau număr întreg, nu versiune semantică. Un API web nu e un pachet. Apelanții nu pot fixa o versiune minor a unui URL, deci v2 sau 2026-08-26 spune tot ce are nevoie un apelant, iar versionarea semantică sugerează o promisiune de compatibilitate pe care schema n-o poate livra.
  2. Țineți versiunea în afara căilor de cod cărora nu le pasă. O versiune ar trebui să selecteze un strat de traducere la margine, nu să ramifice logica de business. Două copii complete ale bazei de cod e cum o versiune ajunge neîntreținută.
  3. Dați fiecărei versiuni un implicit și un document. Apelanții care nu trimit versiune primesc cea mai veche suportată, niciodată cea mai nouă, ca un client nefixat să nu se rupă în ziua lansării. Fiecare versiune are o pagină care spune ce s-a schimbat față de cea anterioară.
  4. Setați o fereastră de suport și publicați-o. Ghidul Google de versionare, AIP-185, cere o perioadă de tranziție rezonabilă și bine comunicată și recomandă 180 de zile chiar și pentru funcționalitățile beta. Alegeți o fereastră, scrieți-o, și aplicați-o fără renegociere per versiune.
  5. Pensionați versiunile așa cum pensionați endpoint-uri. O versiune trecută de fereastra ei primește același tratament ca orice API depreciat: un anunț, un header Sunset (RFC 8594) în fiecare răspuns, o reamintire la jumătatea drumului către apelanții rămași, și o dată de eliminare care se respectă.

Ce sunt v1 și v2 într-un API REST?

v1 și v2 sunt nume pentru două contracte pe care același server le suportă simultan. Un v2 există pentru că ceva în v1 nu putea fi schimbat fără a rupe apelanții săi, deci schimbarea a mers într-un contract nou, iar cel vechi a continuat să funcționeze. Numerele nu sugerează că v2 e complet sau că v1 e mort; ambele sunt adevărate doar dacă documentația o spune. Un v3 care apare în fiecare trimestru e un semn că se versionează schimbări aditive, sau că contractul n-a fost niciodată proiectat să absoarbă schimbare. gRPC rezolvă aceeași problemă diferit: schimbări de API gRPC și Protobuf acoperă versionarea prin numele pachetului dintr-un fișier .proto în loc de o cale URL, și un format pe wire unde redenumirea unui câmp e gratis dar renumerotarea lui e o schimbare care rupe compatibilitatea pe care niciun apelant REST n-ar recunoaște-o ca riscantă.

Ce ar trebui să anunțe o schimbare de versiune?

O schimbare de versiune ar trebui să anunțe ce se rupe, pe cine afectează, cum să migreze, și cât timp continuă să funcționeze versiunea anterioară. Intrarea are aceeași formă ca orice altă intrare de schimbare care rupe compatibilitatea, plus o linie care declară fereastra de suport. Iată una pentru un API versionat prin header:

Versiunea API 2026-11-01 e disponibilă. Versiunea 2025-06-15 e suportată până pe 1 noiembrie 2027. Nou în 2026-11-01: GET /invoices returnează amount în cele mai mici unități ca un număr întreg în loc de string zecimal, iar câmpul depreciat customer_name e eliminat în favoarea obiectului customer. Afectează apelanții pe 2025-06-15 care analizează amount ca string, ceea ce e implicit pentru clienții nefixați creați înainte de iunie 2025. Migrare: analizați amount ca număr întreg și citiți numele din customer.name. Fixați X-Api-Version: 2026-11-01 când sunteți gata. Nimic nu se schimbă pentru apelanții care nu fixează versiunea.

Ultima propoziție e cea care le lasă pe majoritatea cititoarelor să se oprească din citit, și aparține fiecărui anunț de versiune. Pagina exemple de changelog include intrări de la API-uri care versionează astfel, iar diferența dintre cele bune și restul stă majoritar în acea ultimă propoziție.

Cine e anunțat când o versiune se schimbă?

Toată lumea de pe versiunea veche, individual, și changelog-ul pentru toți ceilalți. O schimbare de versiune e singurul caz în care “am postat ceva despre asta” garantat ratează exact apelanții care contează: cei care au fixat o versiune acum doi ani și n-au mai citit o notă de lansare de atunci. Datele de utilizare răspund cine sunt ei; notificarea trebuie să-i ajungă unde e codul lor, în header-ele de răspuns și într-un mesaj către proprietara contului.

În bucla pe care o rulăm, intrarea care anunță o versiune e redactată din pull request-ul care o lansează, revizuită de o persoană, și publicată pe flux și widget, unde un client versionat o poate citi ca JSON. Oricine al cărui feedback din widget a cerut schimbarea, sau a raportat eroarea pe care o rezolvă, și a devenit un issue GitHub pe care pull request-ul îl închide, e anunțat pe acel issue de îndată ce intrarea intră în direct. Mecanismul e același ca pentru orice intrare; un salt de versiune e doar intrarea cu cea mai mare miză.

FAQ

Ar trebui fiecare schimbare de API să primească o versiune nouă? Nu. Doar schimbările care rup compatibilitatea. Schimbările aditive sunt lansate sub versiunea curentă cu o intrare de changelog. Versionarea schimbărilor aditive antrenează apelanții să ignore versiunile.

E versionarea prin URL mai bună decât cea prin header? Versionarea prin URL e mai ușor de văzut pentru apelanți și mai greu pentru voi să o evoluați treptat; versionarea prin header e invers. Pentru un API public cu mulți clienți mici, versionarea prin URL eșuează mai puțin. Pentru un API mare cu strat de traducere, versiunea datată prin header scalează mai bine.

Câte versiuni ar trebui suportate simultan? Cât mai puține permite fereastra voastră de suport, și niciodată un număr nelimitat. Două sau trei versiuni concurente e normal; mai mult de atât înseamnă de obicei că versiunile nu sunt pensionate.

Ce ar trebui să primească cererile fără versiune? Cea mai veche versiune suportată, ca clienții existenți nefixați să continue să funcționeze, cu un header de răspuns care le spune ce versiune au primit.


Afirmațiile tehnice din acest articol nu au fost verificate independent. Dacă ceva nu e corect, spune-ne și vom corecta.

Pe changeloop: Documentație pentru dezvoltatori, Exemple de changelog

changeloop
Echipa care construiește un changelog care închide bucla. Utilizatorii cer ceva, echipa ta livrează, cel care a cerut află.