
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 zeer slecht en kunnen ze een zeer ernstige smet zijn op een anders nuttige API. Een breaking change is een wijziging in het gedrag van een API die de integratie van een gebruiker kan breken en kan resulteren in veel frustratie en verlies van vertrouwen tussen de API-provider en gebruiker. Breaking changes vereisen dat gebruikers vooraf op de hoogte worden gesteld (met bijbehorende mea culpa's) in plaats van een wijziging die gewoon verschijnt, zoals een geweldig nieuwe functie. De manier om die frustratie te voorkomen is door een versie van een API aan te geven met garanties van de API-eigenaar dat er binnen een enkele versie geen verrassende wijzigingen worden geïntroduceerd.
Dus hoe moeilijk kan het zijn om een API te versienummeren? De waarheid is dat het niet moeilijk is, maar wat wel moeilijk is, is enige helderheid te behouden door niet zinloos te vervallen in een duizelingwekkend aantal versies en subversies, toegepast op tientallen API-eindpunten met onduidelijke compatibiliteiten.
We hebben drie jaar geleden v1 van de API geïntroduceerd en hadden niet verwacht dat het tot op de dag van vandaag sterk zou blijven. Dus hoe zijn we erin geslaagd om al meer dan twee jaar de beste e-mailbezorgings-API te bieden, maar nog steeds dezelfde API-versie te behouden? Deze stabiliteit is cruciaal voor ontwikkelaars die applicaties bouwen met e-mail-API's in cloudinfrastructuur, waar betrouwbaarheid en consistentie van het grootste belang zijn. Hoewel er veel verschillende meningen zijn over hoe REST-API's te versienummeren, hoop ik dat het verhaal van onze bescheiden maar krachtige v1 je kan begeleiden op weg naar verlichting in API-versienummering.
REST Is Best
API Governance
Met een gekozen versieconventie hadden we meer vragen. Wanneer zouden we de versie aanpassen? Wat is een breaking change? Zouden we de hele API opnieuw versie nummeren of alleen bepaalde endpoints? Bij SparkPost werken we met meerdere teams aan verschillende delen van onze API. Binnen die teams werken mensen op verschillende momenten aan verschillende endpoints. Daarom is het erg belangrijk dat onze API consistent is in het gebruik van conventies. Dit was groter dan alleen versiebeheer.
We hebben een bestuursgroep opgericht met daarin ingenieurs die elk team vertegenwoordigen, een lid van het Product Management team en onze CTO. Deze groep is verantwoordelijk voor het opstellen, documenteren en handhaven van onze API-conventies over alle teams heen. Een API-governance Slack-kanaal is ook handig voor levendige discussies over dit onderwerp.
De bestuursgroep identificeerde een aantal manieren waarop wijzigingen in de API kunnen worden doorgevoerd die nuttig zijn voor de gebruiker en geen breaking change vormen. Deze omvatten:
Een nieuwe resource of API-endpoint
Een nieuwe optionele parameter
Een wijziging in een niet-openbare API-endpoint
Een nieuwe optionele sleutel in de JSON POST-body
Een nieuwe sleutel geretourneerd in de JSON-respons body
Omgekeerd omvatte 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 endpoint
Verwijdering van een bestaand endpoint-aanvraagmethode
Een materieel ander intern gedrag van een API-aanroep – zoals een wijziging in het standaardgedrag.
The Big 1.0
Terwijl we deze conventies documenteerden en bespraken, kwamen we ook tot de conclusie dat het in ieders belang (inclusief het onze!) was om te voorkomen dat er wijzigingen in de API zouden worden 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 committeerden aan “v1”.
Het verzenden van een eenvoudige e-mail vergde veel te veel moeite. Om “de simpele dingen simpel te houden” hebben we de POST-body bijgewerkt om ervoor te zorgen dat zowel eenvoudige als complexe gebruiksscenario’s mogelijk zijn. Het nieuwe formaat was ook toekomstbestendiger. Ten tweede hebben we een probleem met het Metrics endpoint aangepakt. Dit endpoint gebruikte een “group_by” parameter die het formaat van de GET-responsbody zou wijzigen, zodat de eerste sleutel de waarde van de parameter group by zou zijn. Dat leek niet erg RESTful, dus hebben we elke group by in een apart endpoint gesplitst. Ten slotte hebben we elk endpoint gecontroleerd en hier en daar kleine wijzigingen aangebracht om ervoor te zorgen dat ze voldeden aan de standaarden.
Nauwkeurige Documentatie
Het is belangrijk om nauwkeurige en bruikbare API-documentatie te hebben om breaking changes te vermijden, of ze nu opzettelijk of onbedoeld zijn. We hebben besloten om een eenvoudige benadering van API-documentatie te gebruiken door gebruik te maken van een Markdown-taal genaamd API Blueprint en onze documentatie in Github beheren. Onze gemeenschap draagt bij aan en verbetert deze open source documenten. We onderhouden ook een niet-openbare set documenten in Github voor interne API's en endpoints.
Aanvankelijk publiceerden we onze documenten op Apiary, een geweldig hulpmiddel voor prototyping en publiceren van API-documentatie. Echter, het inbedden van Apiary in onze website werkt niet goed op mobiele apparaten, daarom gebruiken we nu Jekyll om in plaats daarvan statische documenten 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.