Wersjonowanie i polityka zmian
Ta polityka jest częścią kontraktu z integratorem. Opisuje, jak zmieniamy API i jak wcześnie się o tym dowiesz.
- Wersja główna w adresie. Ścieżki zaczynają się od
/v1, np.https://api.rezeri.com/v1/bookings. Nie wersjonujemy API datami. - W
v1tylko zmiany addytywne. Dodajemy endpointy, pola odpowiedzi, opcjonalne pola żądania, wartości enumów i typy zdarzeń webhooków. Niczego nie usuwamy ani nie zmieniamy łamiąco. - Zmiana łamiąca = nowa wersja główna (
v2). Poprzednią wersję główną wspieramy co najmniej 12 miesięcy od udostępnienia następnej. Wygaszane endpointy będą zwracać nagłówkiDeprecation(RFC 9745) iSunset(RFC 8594). Właściciele kluczy API, które wołały wygaszany endpoint w ostatnich 30 dniach, dostaną e-mail. - Beta. Endpoint oznaczony w OpenAPI
x-beta: truei etykietą „Beta” w referencji może zmienić się łamiąco. Ogłaszamy to z 30-dniowym wyprzedzeniem w changelogu i e-mailem. Endpoint jest w becie najwyżej 90 dni, potem przechodzi do stabilnegov1albo zostaje usunięty. Dziś wv1nie ma endpointów beta. - Automatyczna bramka. Każda zmiana kodu porównuje schemat OpenAPI z poprzednią wersją (
oasdiff breaking). Zmiana łamiąca w stabilnymv1zatrzymuje ją na przeglądzie kodu, zanim trafi do wydania. Każda zmiana publicznego schematu wymaga wpisu w changelogu. - Rytm wydań. API wychodzi razem z wydaniami aplikacji Rezeri, bez osobnego cyklu.
- Warstwa izolująca. Publiczne API ma własne schematy i nie korzysta ze schematów wewnętrznego API aplikacji. Zmiana wewnętrzna nie zmienia publicznego kontraktu — pilnuje tego bramka z punktu 5.
- Wyjątek: bezpieczeństwo. Poprawka błędu bezpieczeństwa może od razu zawęzić zachowanie API, wbrew punktom 2 i 3. Opisujemy ją w changelogu.
Twoja strona kontraktu: tolerancyjny odczyt
Dział zatytułowany „Twoja strona kontraktu: tolerancyjny odczyt”Zmiany addytywne nie wymagają od nas nowej wersji, więc Twoja integracja musi je znosić. To wymóg, nie zalecenie:
- Ignoruj nieznane pola w odpowiedziach i w zdarzeniach webhooków.
- Obsłuż nieznaną wartość enuma, np. nowy
statusrezerwacji. Nie przerywaj przetwarzania — potraktuj ją jak stan, którego nie znasz. - Ignoruj nieznane typy zdarzeń webhooków.
- Nie polegaj na kolejności pól w JSON ani na kolejności zdarzeń.
Body żądań działa odwrotnie: nieznane pole daje 422 unknown_field. Nowego opcjonalnego pola żądania użyj dopiero wtedy, gdy pojawi się w changelogu i referencji.
Co jest zmianą łamiącą
Dział zatytułowany „Co jest zmianą łamiącą”Zmiany łamiące — w stabilnym v1 nie zrobimy ich, wymagają v2:
- usunięcie albo zmiana nazwy endpointu, pola odpowiedzi czy parametru,
- zmiana typu lub formatu pola (np. kwota ze stringa na liczbę),
- nowe pole wymagane w żądaniu albo zmiana pola opcjonalnego na wymagane,
- zaostrzenie walidacji żądania (np. krótsza maksymalna długość, węższy zakres),
- usunięcie wartości enuma, którą wysyłasz w żądaniu,
- zmiana wymaganego scope klucza,
- zmiana znaczenia istniejącego pola albo statusu,
- zmiana koperty zdarzenia webhooka (
id,type,created_at,api_version,data.object) albo schematu podpisuv1wRezeri-Signature.
Zmiany niełamiące — mogą pojawić się w v1 w każdym wydaniu:
- nowy endpoint,
- nowe pole w odpowiedzi albo w
data.objectzdarzenia, - nowe opcjonalne pole żądania albo opcjonalny parametr zapytania,
- nowa wartość enuma w odpowiedzi,
- nowy typ zdarzenia webhooka,
- nowy nagłówek odpowiedzi,
- poluzowanie walidacji żądania.
Endpointy beta
Dział zatytułowany „Endpointy beta”Endpoint beta pozwala nam szybko rozwijać nową funkcję, zanim ustalimy jej kontrakt. Bramka z punktu 5 przepuszcza łamiące zmiany w endpointach beta, ale nie przepuszcza usunięcia endpointu — także beta. Usunięcie endpointu beta to więc zawsze świadoma decyzja, którą ogłaszamy w changelogu i e-mailem co najmniej 30 dni wcześniej.
Jeśli integrujesz się z endpointem beta, śledź changelog i licz się ze zmianą w ciągu 90 dni.
Gdzie śledzić zmiany
Dział zatytułowany „Gdzie śledzić zmiany”- Changelog — każda zmiana publicznego schematu, z datą.
- Pole
api_versionw każdym zdarzeniu webhooka podaje wersję, w której opisany jestdata.object.