
Quindi, quanto può essere difficile gestire le versioni di un'API? La verità è che non è così difficile, ma ciò che è complicato è mantenere un po' di sanità mentale evitando di cadere inutilmente in un numero vertiginoso di versioni e sotto-versioni applicate a dozzine di endpoint API con compatibilità poco chiare.
I Cambiamenti Bruschi sono Male! API Versioning Buono!
Come chiunque abbia costruito o utilizzi regolarmente un'API si rende conto presto o tardi, i cambiamenti drastici sono molto negativi e possono essere una macchia molto seria su un'API altrimenti utile. Un cambiamento drastico è una modifica al comportamento di un'API che può interrompere l'integrazione di un utente e comportare molta frustrazione e perdita di fiducia tra il fornitore e l'utente dell'API. I cambiamenti drastici richiedono che gli utenti vengano avvisati in anticipo (con relative scuse) piuttosto che un cambiamento che appare improvvisamente, come una nuova caratteristica piacevole. Il modo per evitare tale frustrazione è di versionare un'API con garanzie da parte del proprietario dell'API che non verranno introdotti cambiamenti sorprendenti all'interno di una singola versione.
Quindi quanto può essere difficile versionare un'API? La verità è che non lo è, ma ciò che è difficile è mantenere una certa sanità mentale non degenerando inutilmente in un numero vertiginoso di versioni e sottoversioni applicate a decine di endpoint dell'API con compatibilità poco chiare.
Abbiamo introdotto v1 dell'API tre anni fa e non ci siamo resi conto che sarebbe andata così forte fino ad oggi. Allora, come abbiamo continuato a fornire la migliore email delivery API per oltre due anni mantenendo ancora la stessa versione dell'API? Mentre ci sono molte opinioni diverse su come versionare le REST API, spero che la storia della nostra umile ma potente v1 possa guidarti sulla strada dell'illuminazione del versioning delle API.
REST È Best
API Governance
Con una convenzione di versioning selezionata, avevamo più domande. Quando avremmo incrementato la versione? Che cos'è un cambiamento determinante? Reviseremmo l'intero API o solo alcuni endpoint? In SparkPost, abbiamo più team che lavorano su diverse parti del nostro API. All'interno di questi team, le persone lavorano su diversi endpoint in tempi diversi. Pertanto, è molto importante che il nostro API sia coerente nell'uso delle convenzioni. Questo era più grande del versioning.
Abbiamo istituito un gruppo di governance includendo ingegneri rappresentanti ogni team, un membro del team di Product Management e il nostro CTO. Questo gruppo è responsabile dell'istituzione, documentazione e applicazione delle nostre convenzioni API tra tutti i team. Un canale Slack di governance delle API è anche utile per dibattiti animati sull'argomento.
Il gruppo di governance ha identificato una serie di modi in cui le modifiche possono essere introdotte nell'API che sono vantaggiose per l'utente e non costituiscono un cambiamento determinante. Questi includono:
Una nuova risorsa o endpoint API
Un nuovo parametro opzionale
Una modifica a un endpoint API non pubblico
Una nuova chiave opzionale nel corpo POST JSON
Una nuova chiave restituita nel corpo di risposta JSON
Al contrario, un cambiamento determinante includeva qualsiasi cosa che potesse interrompere un'integrazione dell'utente come:
Un nuovo parametro obbligatorio
Una nuova chiave obbligatoria nei corpi POST
Rimozione di un endpoint esistente
Rimozione di un metodo di richiesta endpoint esistente
Un comportamento interno materialmente diverso di una chiamata API – come un cambiamento al comportamento predefinito.
The Big 1.0
Come abbiamo documentato e discusso queste convenzioni, siamo anche giunti alla conclusione che fosse nell'interesse di tutti (incluso il nostro!) evitare di apportare cambiamenti dirompenti all'API poiché la gestione di più versioni aggiunge un bel po' di impegno. Abbiamo deciso che c'erano alcune cose che avremmo dovuto sistemare con la nostra API prima di impegnarci nel “v1”.
Inviare una semplice email richiedeva troppo sforzo. Per “mantenere le cose semplici”, abbiamo aggiornato il corpo del POST per garantire che entrambi i casi d'uso, semplici e complessi, siano accomodati. Il nuovo formato era anche più a prova di futuro. In secondo luogo, abbiamo affrontato un problema con il Metrics endpoint. Questo endpoint utilizzava un parametro “group_by” che avrebbe modificato il formato del corpo di risposta GET in modo tale che la prima chiave sarebbe stata il valore del parametro di gruppo. Non sembrava molto RESTful, quindi abbiamo suddiviso ogni gruppo in un endpoint separato. Infine, abbiamo controllato ciascun endpoint e apportato alcune modifiche qua e là per garantire che fossero conformi agli standard.
Documentazione Accurata
È importante avere documentazione API accurata e utilizzabile per evitare modifiche che interrompono, di tipo deliberato o involontario. Abbiamo deciso di utilizzare un approccio semplice alla documentazione API sfruttando un linguaggio Markdown chiamato API Blueprint e gestire i nostri documenti su Github. La nostra comunità contribuisce e migliora questi documenti open source. Manteniamo anche un set non pubblico di documenti su Github per API e endpoint interni.
Inizialmente, abbiamo pubblicato i nostri documenti su Apiary, un ottimo strumento per prototipare e pubblicare documenti API. Tuttavia, l'inclusione di Apiary nel nostro sito web non funziona sui dispositivi mobili, quindi ora utilizziamo Jekyll per generare invece documenti statici. I nostri ultimi SparkPost API docs ora si caricano rapidamente e funzionano bene sui dispositivi mobili, il che è importante per i sviluppatori che non sono sempre seduti al loro computer.
Separare Deployment da Release
Abbiamo imparato presto il prezioso trucco di separare un deployment da un rilascio. In questo modo è possibile distribuire frequentemente modifiche quando sono pronte attraverso consegna e distribuzione continue, ma non le annunciamo sempre pubblicamente o le documentiamo allo stesso tempo. Non è raro per noi distribuire un nuovo endpoint API o un miglioramento a un endpoint API esistente e utilizzarlo dall'interno dell'interfaccia utente o con strumenti interni prima di documentarlo pubblicamente e supportarlo. In questo modo possiamo apportare alcune modifiche per la facilità d'uso o la conformità agli standard senza preoccuparci di creare un temuto cambiamento che rompa. Una volta che siamo soddisfatti del cambiamento, lo aggiungiamo alla nostra documentazione pubblica.
Accidenti!
È giusto ammettere che ci sono stati momenti in cui non siamo stati fedeli ai nostri ideali di “no breaking changes” e vale la pena imparare da questi. In un'occasione abbiamo deciso che sarebbe stato meglio per gli utenti se una determinata proprietà fosse predefinita su true invece che su false. Dopo aver implementato la modifica, abbiamo ricevuto diverse lamentele dagli utenti poiché il comportamento era cambiato inaspettatamente. Abbiamo annullato la modifica e aggiunto un'impostazione a livello di account, un approccio sicuramente più user-friendly.
Occasionalmente siamo tentati di introdurre breaking changes come risultato di correzioni di bug. Tuttavia, abbiamo deciso di lasciare queste idiosincrasie piuttosto che rischiare di compromettere le integrazioni dei clienti per il bene della coerenza.
Ci sono rari casi in cui abbiamo preso la seria decisione di apportare una modifica che rompe qualcosa, come deprecare una risorsa o un metodo API, nell'interesse della comunità utente più ampia e solo dopo aver confermato che c'è poco o nessun impatto sugli utenti. Ad esempio, abbiamo deliberatamente scelto di modificare il comportamento di risposta della Suppression API, ma solo dopo aver attentamente valutato i benefici e gli impatti per la comunità e comunicato attentamente la modifica ai nostri utenti. Tuttavia, non introduceremmo mai una modifica che abbia una remota possibilità di influenzare direttamente l'invio di un’email di produzione dell’utente.