
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 prima o poi, le modifiche rompenti sono molto dannose e possono essere un serio difetto in un API altrimenti utile. Una modifica rompente è un cambiamento nel comportamento di un API che può interrompere l'integrazione di un utente e causare molta frustrazione e perdita di fiducia tra il fornitore e l'utente dell'API. Le modifiche rompenti richiedono che gli utenti siano avvisati in anticipo (con relative scuse) piuttosto che un cambiamento che appare semplicemente, come un nuovo e delizioso miglioramento. La via per evitare quella frustrazione è quella di versionare un API con la garanzia dal proprietario dell'API che non ci saranno modifiche sorprendenti introdotte all'interno di una singola versione.
Allora quanto può essere difficile versionare un API? La verità è che non lo è, ma ciò che è difficile è mantenere una certa sanità mentale evitando di cadere inutilmente in un numero vertiginoso di versioni e sotto-versioni applicate su dozzine di endpoint API con compatibilità poco chiare.
Abbiamo introdotto v1 dell'API tre anni fa e non ci siamo resi conto che sarebbe stato forte fino ad oggi. Quindi come abbiamo continuato a fornire il miglior API per la consegna di email per oltre due anni ma mantenere ancora la stessa versione API? Questa stabilità è cruciale per gli sviluppatori che costruiscono applicazioni con email APIs in cloud infrastructure, dove affidabilità e coerenza sono fondamentali. Sebbene ci siano molte opinioni diverse su come versionare i REST APIs, spero che la storia del nostro umile e potente v1 possa guidarti sulla strada per l'illuminazione della versionizzazione di API.
REST È Best
API Governance
Con una convenzione di versioning selezionata abbiamo avuto più domande. Quando dovremmo incrementare la versione? Cos'è un cambiamento significativo? Reviseremo l'intera API o solo alcuni endpoint? In SparkPost, abbiamo più team che lavorano su diverse parti della nostra API. All'interno di quei team, le persone lavorano su diversi endpoint in momenti diversi. Pertanto, è molto importante che la nostra API sia coerente nell'uso delle convenzioni. Questo era più grande rispetto al versioning.
Abbiamo istituito un gruppo di governance che include ingegneri che rappresentano ciascun team, un membro del team di Product Management e il nostro CTO. Questo gruppo è responsabile dell'istituzione, della documentazione e dell'applicazione delle nostre convenzioni API in tutti i team. Un canale di governance API Slack è anche utile per dibattiti animati sul tema.
Il gruppo di governance ha identificato diversi modi in cui i cambiamenti possono essere introdotti nell'API che sono benefici per l'utente e non costituiscono un cambiamento significativo. Questi includono:
Una nuova risorsa o endpoint API
Un nuovo parametro opzionale
Un cambiamento a un endpoint API non pubblico
Una nuova chiave opzionale nel corpo POST JSON
Una nuova chiave restituita nel corpo della risposta JSON
Al contrario, un cambiamento significativo includeva qualsiasi cosa che potesse interrompere l'integrazione dell'utente come:
Un nuovo parametro richiesto
Una nuova chiave richiesta nei corpi POST
Rimozione di un endpoint esistente
Rimozione di un metodo di richiesta di endpoint esistente
Un comportamento interno materialmente diverso di una chiamata API – come un cambiamento nel comportamento predefinito.
The Big 1.0
Mentre documentavamo e discutevamo queste convenzioni, siamo giunti anche alla conclusione che era nel migliore interesse di tutti (inclusi noi!) evitare di apportare modifiche che interrompessero l'API, poiché la gestione di più versioni aggiunge un bel po' di carico. Abbiamo deciso che c'erano alcune cose che dovevamo sistemare con la nostra API prima di impegnarci su "v1".
Inviando un semplice email richiedeva troppo sforzo. Per "mantenere le cose semplici", abbiamo aggiornato il corpo POST per garantire che siano accettati sia casi d'uso semplici che complessi. Il nuovo formato era anche più a prova di futuro. In secondo luogo, abbiamo affrontato un problema con l'endpoint Metrics. Questo endpoint utilizzava un parametro "group_by" che modificava il formato del corpo di risposta GET in modo tale che la prima chiave fosse il valore del parametro group by. Non sembrava molto RESTful, quindi abbiamo suddiviso ciascun group by in un endpoint separato. Infine, abbiamo verificato ogni endpoint e abbiamo apportato piccole 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 il funzionamento, siano esse deliberate o involontarie. 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 di documenti non pubblico 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'integrazione di Apiary nel nostro sito web non funziona sui dispositivi mobili, quindi ora usiamo Jekyll per generare documenti statici. I nostri ultimi SparkPost API docs ora si caricano rapidamente e funzionano bene sui dispositivi mobili, il che è importante per gli sviluppatori che non sono sempre alla scrivania.
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.