API prawie nigdy nie pozostaje bez zmian. Pojawiają się nowe funkcje, reguły biznesowe i klienci, których nie można zaktualizować jednocześnie. JSON ułatwia dodanie kolejnego pola, ale sam nie gwarantuje kompatybilności. Trwały interfejs potrzebuje stabilnej semantyki, reguł ewolucji i obserwowalności pokazującej, z których części kontraktu ktoś nadal korzysta.
Znaczenie pola jest ważniejsze niż jego typ
String może pozostać stringiem, a mimo to złamać klienta, jeśli zmieni znaczenie. Status active nie powinien nagle opisywać innego etapu, a cena nie może po cichu zacząć zawierać podatku. Takie zmiany nie powodują błędu parsera, przez co są szczególnie niebezpieczne.
Jednostki, strefy czasowe i definicje powinny być dokumentowane. Nowe pojęcie zwykle zasługuje na nowe pole i okres migracji.
Zmiany addytywne są zwykle najbezpieczniejsze
Dodanie opcjonalnej właściwości w odpowiedzi nie przeszkadza klientowi ignorującemu nieznane pola. Usunięcie, zmiana nazwy, zmiana typu lub nowy obowiązkowy element requestu są najczęściej breaking changes.
Nowa wartość enum też może złamać zamknięty switch. SDK i aplikacje powinny mieć kontrolowany fallback dla nieznanych wartości, jeżeli serwer ma rozwijać listę bez nowej wersji.
Brak, null i wartość pusta muszą być rozróżnione
W częściowej aktualizacji brak pola może oznaczać „nie zmieniaj”, null „usuń wartość”, a pusty string „ustaw pustą wartość”. Jeśli endpoint miesza te znaczenia, klienci tracą dane albo nie potrafią ich wyczyścić.
Kontrakt powinien opisywać każdy stan. Dla złożonych zmian czasem czytelniejszy jest format operacji niż obiekt pełen wieloznacznych nulli.
Odpowiedzi potrzebują stabilnego kształtu
Lista nie powinna być obiektem dla jednego elementu i tablicą dla wielu. ID nie powinno raz być liczbą, a raz stringiem. Każdy wyjątek zmusza wszystkie integracje do dodania rozgałęzienia.
Puste kolekcje zwykle warto zwracać jako puste tablice lub obiekty. Konsystencja jest bardziej wartościowa niż oszczędzenie dwóch znaków.
Błędy są częścią publicznego API
Tekst wiadomości jest dobry dla człowieka, ale słaby jako warunek w kodzie. Klient potrzebuje stabilnego kodu błędu, odpowiedniego statusu HTTP i struktury szczegółów walidacji. Treść może zostać poprawiona lub przetłumaczona bez łamania logiki.
Stack trace, SQL i sekrety nie powinny trafiać do odpowiedzi. Request ID łączy bezpieczny komunikat z dokładną telemetryką po stronie serwera.
Paginacja musi wytrzymać ruch danych
Offset jest prosty, ale równoległe inserty powodują pominięcia i duplikaty między stronami. Cursor oparty na stabilnym sortowaniu lepiej radzi sobie z aktywnym zbiorem. Klient powinien traktować go jako opaque value.
Sortowanie wymaga tie-breakera. Sam timestamp nie wystarcza, gdy kilka rekordów ma tę samą wartość. Dodatkowe unikalne ID utrzymuje deterministyczną granicę.
Wersjonowanie ma koszt operacyjny
Nowa główna wersja daje przestrzeń na zmianę niekompatybilną, ale podwaja dokumentację, testy i czas utrzymania. Jeśli każda drobna korekta tworzy wersję, warianty rosną szybciej niż można je wyłączyć.
Zmiany addytywne powinny pozostać podstawowym narzędziem. Gdy breaking change jest konieczny, migracja potrzebuje harmonogramu, metryk użycia i czytelnego changelogu.
Request może być bardziej rygorystyczny niż response
Klient powinien zwykle ignorować nowe pola odpowiedzi. Serwer może odrzucać nieznane pola requestu, aby wykryć literówki i błędne założenia. Taka asymetria wspiera rozwój i chroni operacje zapisujące.
Proxy, które zachowuje dane bez ich posiadania, może potrzebować innej polityki. Ważne, aby zachowanie wynikało z roli systemu, a nie z przypadkowego ustawienia serializera.
Idempotency chroni przed niepewnymi retry
Po timeout klient nie wie, czy serwer utworzył zamówienie. Idempotency-Key pozwala powtórzyć tę samą intencję bez drugiego skutku. Serwer wiąże klucz, fingerprint requestu i wynik przez określony czas.
Ten sam klucz z innym body powinien wywołać konflikt. Generowanie nowego ID przy każdym retry nie rozwiązuje problemu, bo opisuje nową próbę zamiast tej samej operacji.
Schema powinno być wykonywalnym źródłem prawdy
OpenAPI lub JSON Schema może opisywać typy, wymagane pola, formaty i limity. Największą wartość daje wtedy, gdy zasila walidację, dokumentację i contract tests. Ręcznie utrzymywana specyfikacja, która odbiega od produkcji, jest niebezpieczna.
Przykłady powinny obejmować puste listy, null, duże ID, nieznany enum i odpowiedzi błędów. Idealny happy path nie uczy odpornej integracji.
Testy konsumentów ujawniają realne zależności
Serwer może spełniać schema, a mimo to złamać ważnego klienta, który polega na nieudokumentowanym zachowaniu. Consumer-driven contracts pokazują takie założenia. Nie powinny jednak zamrażać kolejności pól czy whitespace.
Test ma chronić publiczne znaczenie, nie przypadkowy wynik konkretnej biblioteki serializującej.
Usunięcie wymaga dowodu braku użycia
Przed skasowaniem pola warto wiedzieć, które API keys, wersje SDK lub aplikacje nadal je pobierają. Metryki i komunikacja zmniejszają ryzyko. W publicznym API pełna pewność jest trudna, więc okresy deprecation bywają długie.
Deprecated funkcja powinna działać poprawnie do ustalonego końca. Celowe pogarszanie jakości tworzy losowe awarie zamiast kontrolowanej migracji.
Cache musi uwzględniać reprezentację i uprawnienia
Zmiana kształtu odpowiedzi może pozostawić stare obiekty w CDN lub cache aplikacji. Klucz powinien uwzględniać wersję, parametry wpływające na body oraz właściwy kontekst autoryzacji. ETag i warunkowe requesty pomagają klientom odświeżać dane bez pobierania całości.
Podczas migracji trzeba określić, czy stary cache może być odczytany przez nowy kod. Inaczej poprawny deployment daje mieszane odpowiedzi zależnie od wcześniejszego ruchu.
Odporność na zmiany jest procesem zespołu
Review API powinno sprawdzać kompatybilność, limity, prywatność i model błędów. Changelog oraz przewodnik migracji muszą pojawić się przed zmianą zachowania. Właściciel kontraktu odpowiada za cały okres przejściowy.
Dobre JSON API jest przewidywalne: stare pola zachowują znaczenie, nowe są addytywne, breaking changes mają wersję, a wyłączenie opiera się na obserwowanej migracji. Ta dyscyplina oszczędza lata wyjątków po stronie klientów.