
Jak trudne może być wersjonowanie API? Prawda jest taka, że nie jest to trudne, ale to, co jest trudne, to utrzymanie pewnej zdroworozsądkowej perspektywy, aby nie bezsensownie zbaczać w oszałamiającą liczbę wersji i podwersji stosowanych w dziesiątkach punktów końcowych API o niejasnych kompatybilnościach.
Złe zmiany! API Versioning dobre!
Jak każdy, kto budował lub regularnie korzysta z API, prędzej czy później zdaje sobie sprawę, łamanie zmian jest bardzo złe i może być bardzo poważną skazą na inaczej przydatnym API. Łamiąca zmiana to zmiana w zachowaniu API, która może zakłócić integrację użytkownika i powodować dużo frustracji oraz utratę zaufania między dostawcą API a użytkownikiem. Łamiące zmiany wymagają powiadomienia użytkowników z wyprzedzeniem (wraz z towarzyszącymi przeprosinami), a nie zmiany po prostu pojawiającej się, takiej jak przyjemna nowa funkcja. Sposobem na uniknięcie tej frustracji jest wersjonowanie API z zapewnieniem od właściciela API, że nie zostaną wprowadzone żadne zaskakujące zmiany w ramach żadnej pojedynczej wersji.
Więc jak trudne może być wersjonowanie API? Prawda jest taka, że nie jest to trudne, ale trudne jest utrzymanie pewnej stabilności przez unikanie niepotrzebnego dewolucji w zawrotną liczbę wersji i podwersji stosowanych w dziesiątkach punktów końcowych API z niejasnymi kompatybilnościami.
Wprowadziliśmy v1 API trzy lata temu i nie zdawaliśmy sobie sprawy, że będzie ono działało mocno do dziś. Jak więc zdołaliśmy nadal dostarczać najlepsze API do wysyłania e-maili przez ponad dwa lata, ale wciąż utrzymując tę samą wersję API? Podczas gdy istnieje wiele różnych opinii na temat wersjonowania REST API, mam nadzieję, że historia naszego skromnego, ale potężnego v1 może cię poprowadzić na drodze do oświecenia w wersjonowaniu API.
REST Is Best
API Governance
Z wybraną konwencją wersjonowania mieliśmy więcej pytań. Kiedy powinniśmy zwiększyć wersję? Co to jest zmiana łamiąca? Czy przerejestrowalibyśmy całe API, czy tylko niektóre końcówki? W SparkPost mamy wiele zespołów pracujących nad różnymi częściami naszego API. W tych zespołach ludzie pracują nad różnymi końcówkami w różnym czasie. Dlatego bardzo ważne jest, aby nasze API było spójne w używaniu konwencji. To było większe niż wersjonowanie.
Utworzyliśmy grupę zarządzającą, w tym inżynierów reprezentujących każdy zespół, członka zespołu zarządzania produktem i naszego CTO. Ta grupa jest odpowiedzialna za ustanawianie, dokumentowanie i egzekwowanie naszych konwencji API we wszystkich zespołach. Kanał Slack dotyczący zarządzania API także jest pomocny przy żywych debatach na ten temat.
Grupa zarządzająca zidentyfikowała szereg sposobów wprowadzania zmian w API, które są korzystne dla użytkownika i nie stanowią zmiany łamiącej. Obejmuje to:
Nowe źródło lub końcówka API
Nowy opcjonalny parametr
Zmiana w niepublicznej końcówce API
Nowy opcjonalny klucz w JSON POST body
Nowy klucz zwrócony w JSON response body
Natomiast zmiana łamiąca zawierała wszystko, co mogło złamać integrację użytkownika, takie jak:
Nowy wymagany parametr
Nowy wymagany klucz w POST bodies
Usunięcie istniejącej końcówki
Usunięcie istniejącej metody żądania końcówki
Zależnie różne wewnętrzne działanie wywołania API – na przykład zmiana domyślnego działania.
The Big 1.0
Podczas gdy dokumentowaliśmy i omawialiśmy te konwencje, doszliśmy również do wniosku, że w interesie wszystkich (w tym naszym!) jest unikanie wprowadzania zmian łamiących w API, ponieważ zarządzanie wieloma wersjami dodaje sporo obciążeń. Zdecydowaliśmy, że przed zobowiązaniem się do „v1” powinniśmy naprawić kilka rzeczy w naszym API.
Wysłanie prostego e-maila wymagało zbyt dużo wysiłku. Aby „utrzymać prostotę rzeczy prostych”, zaktualizowaliśmy treść POST, aby zapewnić, że zarówno proste, jak i złożone przypadki użycia są obsługiwane. Nowy format był również bardziej odporny na przyszłe zmiany. Po drugie, zajęliśmy się problemem z endpointem Metrics. Ten endpoint używał parametru „group_by”, który zmieniał format treści odpowiedzi GET, tak że pierwszy klucz był wartością parametru group_by. To nie wydawało się bardzo RESTful, więc każdy group_by rozdzieliliśmy na oddzielne endpointy. W końcu skontrolowaliśmy każdy endpoint i wprowadziliśmy drobne zmiany tu i tam, aby zapewnić zgodność ze standardami.
Dokumentacja Accurate
Ważne jest posiadanie dokładnej i użytecznej dokumentacji API, aby uniknąć niezamierzonych lub celowych zmian powodujących problemy. Zdecydowaliśmy się użyć prostego podejścia do dokumentacji API, wykorzystując język Markdown zwany API Blueprint i zarządzać naszą dokumentacją na Githubie. Nasza społeczność przyczynia się do ulepszania tych otwartych źródeł dokumentacji. Prowadzimy również niepubliczny zestaw dokumentacji na Githubie dla wewnętrznych API i punktów końcowych.
Początkowo publikowaliśmy naszą dokumentację w Apiary, świetnym narzędziu do prototypowania i publikowania dokumentacji API. Jednakże, osadzenie Apiary w naszej stronie internetowej nie działa na urządzeniach mobilnych, więc teraz używamy Jekyll do generowania statycznych dokumentów zamiast tego. Nasza najnowsza dokumentacja API SparkPost ładuje się teraz szybko i działa dobrze na urządzeniach mobilnych, co jest ważne dla programistów, którzy nie zawsze siedzą przy swoim komputerze.
Separating Deployment from Release
Na początku nauczyliśmy się cennej sztuczki oddzielania wdrożenia od wydania. Dzięki temu możemy często wdrażać zmiany, gdy są gotowe, za pomocą ciągłego dostarczania i wdrażania, ale nie zawsze ogłaszamy je publicznie lub dokumentujemy w tym samym czasie. Nie jest niczym niezwykłym, że wdrażamy nowy punkt końcowy API lub ulepszenie istniejącego punktu końcowego API i używamy go w interfejsie użytkownika lub za pomocą narzędzi wewnętrznych zanim go publicznie udokumentujemy i wspieramy. W ten sposób możemy wprowadzać do niego drobne poprawki pod kątem użyteczności lub zgodności ze standardami, nie martwiąc się o wprowadzanie niechcianych zmian powodujących problemy. Gdy jesteśmy zadowoleni ze zmiany, dodajemy ją do naszej dokumentacji publicznej.
Cholera!
Jest tylko uczciwie przyznać, że były czasy, kiedy nie sprostaliśmy naszym ideałom „bez zmian łamiących” i warto się z nich uczyć. W jednym przypadku uznaliśmy, że lepiej byłoby dla użytkowników, gdyby pewna właściwość domyślnie wynosiła true zamiast false. Po wprowadzeniu zmiany otrzymaliśmy od użytkowników kilka skarg, ponieważ zachowanie zmieniło się nieoczekiwanie. Cofnęliśmy zmianę i dodaliśmy ustawienie na poziomie konta – zdecydowanie bardziej przyjazne podejście dla użytkownika.
Czasami kusi nas wprowadzanie zmian łamiących w wyniku napraw błędów. Jednak zdecydowaliśmy się pozostawić te idiosynkrazje w spokoju, zamiast ryzykować łamanie integracji klientów dla zachowania spójności.
Są rzadkie przypadki, kiedy podjęliśmy poważną decyzję o wprowadzeniu zmiany łamiącej – na przykład wycofanie zasobu lub metody API – w interesie większej społeczności użytkowników i tylko po potwierdzeniu, że ma to niewielki lub żaden wpływ na użytkowników. Na przykład celowo podjęliśmy decyzję o zmianie zachowania odpowiedzi API Suppression, ale dopiero po dokładnym rozważeniu korzyści i wpływu na społeczność oraz dokładnym komunikowaniu zmiany naszym użytkownikom. Jednak nigdy nie wprowadzilibyśmy zmiany, która ma nawet najmniejsze prawdopodobieństwo bezpośredniego wpływu na wysyłanie emaila produkcyjnego użytkownika.