
Dus hoe moeilijk kan het zijn om een API te versies? De waarheid is dat het niet moeilijk is, maar wat wel moeilijk is, is het behouden van enige gezond verstand door niet onnodig te vervallen in een duizelingwekkend aantal versies en subversies die worden toegepast over tientallen API-eindpunten met onduidelijke compatibiliteiten.
Breaking Changes Slecht! API Versioning Goed!
Zoals iedereen die een API heeft gebouwd of regelmatig gebruikt vroeg of laat beseft, zijn breaking changes erg slecht en kunnen ze een ernstige smet zijn op een anderszins nuttige API. Een breaking change is een wijziging in het gedrag van een API die de integratie van een gebruiker kan breken en tot veel frustratie en verlies van vertrouwen tussen de API-aanbieder en gebruiker kan leiden. Breaking changes vereisen dat gebruikers van tevoren op de hoogte worden gesteld (met bijbehorende verontschuldigingen) in plaats van een wijziging die zomaar verschijnt, zoals een heerlijk nieuwe functie. De manier om die frustratie te vermijden, is door een API te versiebeheer met de garantie van de API-eigenaar dat er binnen een enkele versie geen onverwachte wijzigingen zullen worden geïntroduceerd.
Dus hoe moeilijk kan het zijn om een API te versiebeheer? De waarheid is dat het niet moeilijk is, maar wat wel moeilijk is, is het behouden van enige rede door niet onnodig te vervallen in een duizelingwekkend aantal versies en subversies die worden toegepast op tientallen API-eindpunten met onduidelijke compatibiliteiten.
We introduceerden drie jaar geleden v1 van de API en realiseerden ons niet dat het tot op de dag van vandaag zo sterk zou blijven bestaan. Dus hoe hebben we de beste e-mailbezorg-API al meer dan twee jaar kunnen bieden terwijl we nog steeds dezelfde API-versie handhaven? Hoewel er veel verschillende meningen zijn over hoe REST API's te versiebeheer, hoop ik dat het verhaal van onze nederige maar krachtige v1 je kan begeleiden op je weg naar API versiebeheer verlichting.
REST Is Best
API Governance
Met een selectie voor versienummering hadden we meer vragen. Wanneer zouden we de versie verhogen? Wat is een breaking change? Zouden we de hele API opnieuw versienummeren of alleen bepaalde eindpunten? Bij SparkPost werken we met meerdere teams aan verschillende onderdelen van onze API. Binnen die teams werken mensen op verschillende momenten aan verschillende eindpunten. Daarom is het erg belangrijk dat onze API consistent is in het gebruik van conventies. Dit was groter dan alleen versienummering.
We hebben een bestuursgroep opgericht met ingenieurs die elk team vertegenwoordigen, een lid van het Product Management-team, en onze CTO. Deze groep is verantwoordelijk voor het vaststellen, documenteren en handhaven van onze API-conventies over alle teams heen. Een API-governance Slack-kanaal komt ook goed van pas voor levendige discussies over het onderwerp.
De bestuursgroep heeft een aantal manieren geïdentificeerd waarop wijzigingen in de API kunnen worden doorgevoerd die nuttig zijn voor de gebruiker en geen breaking change vormen. Deze omvatten:
Een nieuwe bron of API-eindpunt
Een nieuwe optionele parameter
Een wijziging van een niet-publieke API-eindpunt
Een nieuwe optionele sleutel in de JSON POST-body
Een nieuwe sleutel in de JSON-respons-body
Omgekeerd omvat een breaking change alles wat de integratie van een gebruiker zou kunnen verstoren, zoals:
Een nieuwe vereiste parameter
Een nieuwe vereiste sleutel in POST-bodies
Verwijdering van een bestaand eindpunt
Verwijdering van een bestaand eindpuntaanvraagmethode
Een wezenlijk ander intern gedrag van een API-oproep - zoals een wijziging van het standaardgedrag.
The Big 1.0
Terwijl we deze conventies documenteerden en bespraken, kwamen we ook tot de conclusie dat het in ieders (inclusief ons!) belang was om te voorkomen dat er brekende wijzigingen in de API werden aangebracht, aangezien het beheren van meerdere versies behoorlijk wat overhead met zich meebrengt. We besloten dat er een paar dingen waren die we moesten oplossen met onze API voordat we ons aan "v1" committeerden.
Het verzenden van een eenvoudige e-mail vergde veel te veel moeite. Om "de eenvoudige dingen eenvoudig te houden" hebben we de POST-body bijgewerkt om ervoor te zorgen dat zowel eenvoudige als complexe gebruiksscenario's worden ondersteund. Het nieuwe formaat was ook toekomstbestendiger. Ten tweede hebben we een probleem met het Metrics-uiteinde aangepakt. Dit eindpunt gebruikte een "group_by"-parameter die het formaat van de GET-responsebody zou wijzigen, zodat de eerste sleutel de waarde van de group by-parameter zou zijn. Dat leek niet erg RESTful, dus hebben we elke group by opgedeeld in een apart eindpunt. Ten slotte hebben we elk eindpunt gecontroleerd en hier en daar kleine wijzigingen aangebracht om ervoor te zorgen dat ze aan de normen voldeden.
Nauwkeurige Documentatie
Het is belangrijk om nauwkeurige en bruikbare API-documentatie te hebben om breaking changes, of die nu opzettelijk of onbedoeld zijn, te vermijden. We hebben besloten een eenvoudige API-documentatiebenadering te gebruiken door gebruik te maken van een Markdown-taal genaamd API Blueprint en onze documentatie te beheren in Github. Onze gemeenschap draagt bij aan en verbetert deze open source-documentatie. We onderhouden ook een niet-openbaar set documentatie in Github voor interne API's en eindpunten.
Aanvankelijk publiceerden we onze documentatie naar Apiary, een geweldig hulpmiddel voor het prototypen en publiceren van API-documenten. Het inbedden van Apiary in onze website werkt echter niet op mobiele apparaten, dus we gebruiken nu Jekyll om in plaats daarvan statische documentatie te genereren. Onze nieuwste SparkPost API-documentatie laadt nu snel en werkt goed op mobiele apparaten, wat belangrijk is voor ontwikkelaars die niet altijd achter hun computer zitten.
Deployment scheiden van Release
We leerden al vroeg de waardevolle truc van het scheiden van een implementatie van een release. Op deze manier is het mogelijk om vaak veranderingen door te voeren wanneer ze klaar zijn via continue levering en implementatie, maar we kondigen ze niet altijd publiekelijk aan of documenteren ze niet altijd tegelijkertijd. Het is niet ongewoon voor ons om een nieuwe API-endpoint te implementeren of een verbetering aan een bestaand API-endpoint te doen en deze te gebruiken binnen de UI of met interne tools voordat we het publiekelijk documenteren en ondersteunen. Op die manier kunnen we enkele aanpassingen doen voor bruikbaarheid of naleving van standaarden zonder ons zorgen te maken over het maken van een gevreesde breaking change. Zodra we tevreden zijn met de verandering voegen we het toe aan onze openbare documentatie.
Doh!
Het is alleen eerlijk om toe te geven dat er momenten zijn geweest waarop we onze “geen breaking changes”-idealen niet hebben waargemaakt, en het is de moeite waard om hiervan te leren. Op een gegeven moment besloten we dat het beter zou zijn voor gebruikers als een bepaalde instelling standaard op true stond in plaats van false. Nadat we de wijziging hadden doorgevoerd, ontvingen we verschillende klachten van gebruikers omdat het gedrag onverwachts was veranderd. We draaiden de wijziging terug en voegden een accountniveau-instelling toe – een veel gebruiksvriendelijkere benadering, dat is zeker.
Soms worden we verleid om breaking changes door te voeren als gevolg van bugfixes. Echter, we besloten deze eigenaardigheden met rust te laten in plaats van het riskeren van het verbreken van klantintegraties voor het behoud van consistentie.
Er zijn zeldzame gevallen waarin we de serieuze beslissing namen om een breaking change door te voeren – zoals het afschaffen van een API-resource of -methode – in het belang van de bredere gebruikersgemeenschap en pas na bevestiging dat er weinig tot geen impact is op gebruikers. Bijvoorbeeld, we maakten bewust de keuze om het reactiegedrag van de Suppression API te wijzigen, maar alleen na zorgvuldig afwegen van de voordelen en nadelen voor de gemeenschap en het zorgvuldig communiceren van de wijziging naar onze gebruikers. Echter, we zouden nooit een wijziging introduceren die zelfs maar een geringe mogelijkheid heeft om direct invloed te hebben op het verzenden van een productie-e-mail van een gebruiker.