JSON Toolkit

FAQ dotyczące współpracy zespołowej: kontrakty, konfiguracja i dane testowe

Podsumowanie: Styl nazewnictwa, formaty czasu, semantyka null, kształty wyliczeń, formaty konfiguracji, organizacja danych testowych i konflikty scalania — dwanaście kompromisów, o które zespoły naprawdę się spierają.

Średniozaawansowany~12 min czytaniaOpublikowano 2026-10-30Zaktualizowano 2026-10-30
Okładka FAQ dotyczącego współpracy zespołowej

Najważniejsze: ustal to raz w konwencji zespołu — koperta {"data":…,"meta":…} z kodami statusu HTTP, jeden styl nazewnictwa w całej witrynie, znaczniki czasu RFC 3339 ze strefą czasową i wyliczenia łańcuchowe — a potem rozwijaj każde nowe API według tego samego kontraktu, tylko dodając pola, nigdy ich nie zmieniając ani nie usuwając.

Na te pytania nie ma jednej poprawnej odpowiedzi, ale zespół musi mieć jedną — inaczej każde nowe API rozpoczyna tę samą kłótnię.

1. Kontrakty API

Jaką strukturę powinno zwracać API?

Trzy częste kształty, każdy z kosztem:

Kształt Przykład Zalety Wady
Surowe dane [{...}, {...}] Proste Nie niesie metadanych
Koperta stała {"code":0,"message":"ok","data":{...}} Ujednolicona obsługa błędów Kod biznesowy powiela status HTTP
Koperta zasobu {"data":{...},"meta":{...},"links":{...}} Rozszerzalna, jasna semantyka Jedna warstwa zagnieżdżenia więcej

Dla wewnętrznych API biznesowych preferuj „kopertę zasobu + kody statusu HTTP wyrażające wynik”:

{
  "data": [{ "id": "1", "name": "示例" }],
  "meta": { "total": 128, "page": 2, "pageSize": 20 }
}

Dla błędów używaj kodu statusu HTTP plus strukturalnego ciała błędu, zamiast zagnieżdżać kolejny code:

{
  "error": {
    "type": "validation_failed",
    "message": "请求参数校验失败",
    "details": [
      { "path": "/email", "reason": "格式不正确" }
    ]
  }
}

Kluczowy punkt: kody statusu HTTP już wyrażają sukces lub porażkę, więc kod biznesowy jest zbędny. Gdy dwa systemy współistnieją, niespójności takie jak „201, ale code to 500” są niemal nieuniknione.

Unikaj nagich tablic. Przy tablicy na wierzchołku nie możesz dołączyć paginacji, sum ani kursorów, a dodanie ich później oznacza zmianę każdego wywołującego.

Czy nazwy pól powinny używać snake_case czy camelCase?

Wybierz jeden, stosuj w całej witrynie i nigdy nie mieszaj ich w jednym dokumencie JSON.

Styl Częsty ekosystem Przykład
camelCase JavaScript / TypeScript / Java userName, createdAt
snake_case Python / Ruby / Go (częściowo) / kolumny bazy user_name, created_at
kebab-case Rzadko; klucze z łącznikami są niewygodne w dostępie user-name

Kryteria:

  1. Kto konsumuje ten JSON: wybierz camelCase, gdy dominuje frontend, snake_case, gdy dominują potoki danych i usługi Pythona;
  2. Czy mapuje się bezpośrednio na kolumny bazy: snake_case oszczędza warstwę konwersji przy przekazywaniu struktur tabel bezpośrednio;
  3. Ciągłość istniejących systemów: zostaw to, czego używa istniejące API; spójność bije poprawność.

Jeśli naprawdę musisz konwertować, rób to na granicy (brama lub warstwa żądań frontendu), zamiast rozrzucać wywołania toCamelCase po kodzie biznesowym.

Jaki format powinny mieć pola czasu?

Używaj łańcuchów RFC 3339 / ISO 8601 z przesunięciem strefy czasowej:

{
  "createdAt": "2026-10-30T11:37:57+08:00",
  "updatedAt": "2026-10-30T03:37:57Z"
}

Kluczowe punkty:

  • Uwzględnij strefę czasową: 2026-10-30 11:37:57 bez strefy nieuchronnie pęknie w scenariuszach między strefami;
  • Używaj Z lub przesunięcia konsekwentnie: oba są poprawne, ale nie mieszaj ich w jednym API;
  • Unikaj numerycznych znaczników czasu: czy to sekundy, czy milisekundy, i czy uwzględniają sekundy przestępne, każdy zespół musi to reinterpretować. Gdy wymagana jest zwięzłość, użyj całkowitoliczbowego znacznika w milisekundach, ale przypnij jednostkę w dokumentacji.

Preferuj nazwy pól z sufiksem At, takie jak createdAt / updatedAt / deletedAt, aby na pierwszy rzut oka było oczywiste, że wartość jest punktem w czasie, a nie czasem trwania. Dla czasu trwania używaj timeoutMs, ttlSeconds — jednostkę umieść w nazwie.

Czy null powinno być null, czy pominiętym polem?

Zależy, czy „to pole ma znaczenie”:

Semantyka Forma Uwagi
Pole istnieje, ale nie ma jeszcze wartości "name": null Jasno mówi „sprawdzono i jest puste”
Pole nie ma związku z tym zasobem Pomiń Na przykład zwykły użytkownik nie powinien mieć vipExpireAt
Częściowa aktualizacja (PATCH) Pomiń = nie zmieniaj Tylko null znaczy tu „wyczyść”

Trzeci przypadek jest najczęściej przeoczony. W żądaniu PATCH:

{ "nickname": null }

to znaczy „wyczyść pseudonim”, podczas gdy pominięcie nickname w ciele żądania znaczy „nie dotykaj pseudonimu”. Jeśli traktujesz oba jako „wyczyść”, użytkownik zmieniający e-mail wytrze swój pseudonim.

Jeszcze raz: nie używaj undefined. To nie jest poprawna wartość JSON, a JSON.stringify całkowicie pomija klucz, więc zachowanie odpowiada „pominięciu”, podczas gdy kod wygląda jak „jawnie przekazano pustą wartość”.

Jak nazywać pola logiczne?

Używaj prefiksów is / has / can / should i unikaj negacji.

{
  "isActive": true,
  "hasPassword": false,
  "canEdit": true
}

Trzy lekcje:

  1. Bez negacji: isDisabled: false zmusza czytelnika, by raz to odwrócił; isEnabled: true jest bardziej bezpośrednie. Podwójna negacja (isNotDisabled) to katastrofa;
  2. Nie używaj liczb dla wartości logicznych: "enabled": 1 każe każdemu konsumentowi raz napisać === 1;
  3. Nie używaj łańcuchów: "enable": "true" jest prawdziwe w JavaScript, a if (data.enable) jest prawdziwe także dla "false"; takie błędy są ekstremalnie trudne do wytropienia.

Czy pola wyliczeniowe powinny używać liczb czy łańcuchów?

Używaj łańcuchów, chyba że istnieje wyraźne ograniczenie przechowywania lub przepustowości.

{ "status": "active" }

W porównaniu z { "status": 1 } łańcuchy wygrywają, ponieważ:

  • Logi, przechwycony ruch i bazy danych są czytelne bezpośrednio, bez zaglądania do tabeli mapowania;
  • Dodanie wartości wyliczenia nie zmienia znaczenia istniejących wartości (wstawienie wartości w środku liczbowego wyliczenia przesuwa wszystko);
  • Frontend nie musi utrzymywać mapowania 1 → 'active' w synchronizacji.

Koszt to kilka dodatkowych bajtów, co po kompresji jest niemal pomijalne.

Praktyka towarzysząca: używaj małych liter i podkreśleń dla łańcuchowych wartości wyliczeń (pending_review) i ogranicz je przez enum w Schema, aby niepoprawne wartości były odrzucane na etapie walidacji.

2. Konfiguracja i dane testowe

Czy konfiguracja powinna być JSON, YAML czy TOML?

Wybieraj według celu, a nie preferencji:

Format Dobre do Złe do
JSON Programowego odczytu i zapisu, ściśle ustrukturyzowanych danych, walidacji Schema Częstego ręcznego edytowania (komentarze niedozwolone)
YAML Głęboko zagnieżdżonej konfiguracji wdrożeniowej (K8s) Ręcznie pisane wcięcia są podatne na błędy; yes/no stają się wartościami logicznymi
TOML Płaskiej konfiguracji zależności i narzędzi (Cargo.toml, pyproject.toml) Słabej czytelności przy głębokim zagnieżdżeniu
JSONC Konfiguracji edytora wymagającej komentarzy (tsconfig.json) Przekraczania granic systemów

Częstym błędem zespołów jest przechowywanie konfiguracji biznesowej w YAML — niejawne konwersje typów w YAML (1.0 staje się liczbą, NO staje się wartością logiczną, 2026-10-30 staje się datą) produkują różne wyniki między parserami.

Jeśli konfiguracja musi być rygorystycznie walidowana przez program, wybierz JSON + Schema i dokumentuj komentarze przez pole _comment albo osobną dokumentację.

Jak organizować dane testowe?

Organizuj według „jeden baseline + minimalne diffy”, zamiast kopiować ogromny JSON dla każdego przypadku testowego.

fixtures/
├─ users/
│  ├─ base.json              # Baseline user object
│  ├─ no-email.json          # Only email is missing
│  └─ invalid-age.json       # Only age is out of range
└─ fixtures.ts               # Utility functions for loading and shallow merging
import baseUser from './users/base.json';

export function makeUser(overrides: Partial<User> = {}): User {
  return { ...baseUser, ...overrides };
}

Dzięki temu nowy przypadek testowy musi tylko wypisać różniące się pola, a zmiana pola baseline automatycznie aktualizuje każdy przypadek.

Trzy zasady:

  1. Dane testowe muszą przechodzić walidację Schema: sam baseline powinien być poprawnymi danymi;
  2. Nie eksportuj prawdziwych danych produkcyjnych: numery telefonów, numery dowodów i adresy to dane osobowe;
  3. Dane testowe należą do kontroli wersji, ale muszą pozostać małe: plik powyżej kilkudziesięciu KB zasługuje na przycięcie lub generowanie.

Jak zmniejszyć konflikty scalania w plikach JSON?

JSON łatwo konfliktuje: formater przepływa cały plik naraz, więc dwie osoby zmieniające po jednej linii konfliktują na całym pliku.

Cztery taktyki:

  1. Ujednolić formatowanie i dodać je do .editorconfig: spójne wcięcia, zakończenia wierszy i końcowa nowa linia unikają niepowiązanych diffów;
  2. Sortuj klucze: uzgodnij porządek alfabetyczny, aby nowe pole nie kolidowało z czyimś punktem wstawienia;
  3. Dziel pliki: rozbij jeden gigantyczny config.json na kilka plików według domen, a powierzchnia konfliktów naturalnie się kurczy;
  4. Unikaj commitów „przeformatuj wszystko”: oddziel formatowanie od zmian biznesowych do dwóch commitów, aby przegląd pozostał czytelny.

Jeśli zespół używa plików lock (pnpm-lock.yaml, package-lock.json), nie scalaj ich ręcznie; zamiast tego regeneruj:

git checkout --theirs pnpm-lock.yaml
pnpm install

Jak utrzymywać zgodność wersji?

Zasada brzmi „dodawaj, nie zmieniaj, nie usuwaj”.

Zmiana Zgodność Podejście
Dodanie pola opcjonalnego Zgodne Dodaj; konsumenci ignorują nieznane pola
Dodanie pola wymaganego Niezgodne Podnieś wersję albo zapewnij wartość domyślną
Usunięcie pola Niezgodne Najpierw oznacz jako przestarzałe, obserwuj wywołujących, potem usuń
Zmiana typu pola Niezgodne Dodaj nową nazwę pola i podwójny zapis w okresie przejściowym
Zmiana zbioru wartości wyliczenia Zależy Dodawanie wartości jest zgodne; usuwanie nie

Mechanizmy wspierające:

  • Konsumenci używają „łagodnego parsowania”: ignorują nieznane pola i dostarczają domyślne dla brakujących;
  • Serwer trzyma stare pola przez jakiś czas i zwraca nagłówek Deprecation w odpowiedzi jako wskazówkę;
  • W Schema nie oznaczaj nowych pól jako required, a przestarzałe pola taguj przez deprecated: true.

Jak walidować kontrakty w CI?

Traktuj Schema jako część kodu i waliduj ją przy każdym commicie.

# Validate sample data against the Schema (using ajv-cli)
npx ajv-cli validate -s schemas/user.json -d "fixtures/users/*.json" --strict=false

Uruchamiaj w CI trzy rzeczy:

  1. Poprawność samego Schema: czy deklaracja $schema jest poprawna i czy $ref się rozwiązuje;
  2. Czy dane testowe pasują do Schema: zapewnia, że dane testowe nie dryfują;
  3. Czy kontrakt jest zgodny wstecznie: porównaj z poprzednią wersją i sprawdź usunięte pola lub zmienione typy.

Trzeci punkt wymaga wsparcia narzędziowego (takiego jak openapi-diff lub json-schema-diff); bez niego przynajmniej porównaj skryptem zmiany w tablicach required i zestawach kluczy properties.

3. Szybka ściągawka

Pytanie Zalecenie
Struktura odpowiedzi {"data":..., "meta":...}, z kodami statusu HTTP dla błędów
Styl nazewnictwa camelCase (frontend-first) lub snake_case (data-first), spójnie w całej witrynie
Format czasu Łańcuchy RFC 3339 ze strefą czasową, nazwy pól kończące się At
Null Użyj null dla braku wartości, pomijaj nieistotne pola, pominięcie w PATCH znaczy „nie zmieniaj”
Nazewnictwo logiczne Prefiksy is/has/can, bez negacji, bez 0/1
Wyliczenia Łańcuchy, małe litery z podkreśleniami, dodaj enum w Schema
Format konfiguracji JSON do walidacji programowej, TOML lub JSONC do ręcznej edycji
Dane testowe Jeden baseline + nadpisania minimalnego diffu, poprawne względem Schema
Konflikty scalania Ujednolić formatowanie, sortuj klucze, dziel pliki
Zgodność wersji Dodawaj, nie zmieniaj, nie usuwaj; oznacz jako przestarzałe przed usunięciem