JSON Toolkit

FAQ k týmové spolupráci: kontrakty, konfigurace a fixtures

Shrnutí: Styl pojmenování, formáty času, sémantika null, tvary výčtů, formáty konfigurace, organizace fixtures a konflikty při slučování – dvanáct kompromisů, o kterých se týmy skutečně hádají.

Středně pokročilý~12 min čteníPublikováno 2026-10-30Aktualizováno 2026-10-30
Titulní obrázek článku FAQ k týmové spolupráci

Stručně: vyřešte tyto věci jednou v týmové konvenci – obálka {"data":…,"meta":…} s HTTP stavovými kódy, jeden styl pojmenování v celém webu, časová razítka RFC 3339 s časovým pásmem a řetězcové výčty – a poté každé nové API rozvíjejte proti stejné smlouvě jen přidáváním polí, nikdy jejich změnou nebo smazáním.

Na tyto otázky neexistuje jediná správná odpověď, ale tým musí mít jedinou odpověď – jinak každé nové API znovu spustí stejný spor.

1. Smlouvy API

Jakou strukturu má API vracet?

Tři běžné tvary, každý s cenou:

Tvar Příklad Plusy Mínusy
Surová data [{...}, {...}] Jednoduché Nemůže nést metadata
Pevná obálka {"code":0,"message":"ok","data":{...}} Jednotné zpracování chyb Obchodní kód duplikuje HTTP status
Zdrojová obálka {"data":{...},"meta":{...},"links":{...}} Rozšiřitelná, jasná sémantika Jedna úroveň vnoření navíc

Pro interní obchodní API preferujte „zdrojovou obálku + HTTP stavové kódy vyjadřující výsledek“:

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

Pro chyby použijte HTTP stavový kód plus strukturované tělo chyby, místo abyste vnořovali další code:

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

Klíčový bod: HTTP stavové kódy už vyjadřují úspěch nebo selhání, takže obchodní kód je nadbytečný. Když oba systémy koexistují, nekonzistence jako „201, ale code je 500“ jsou téměř nevyhnutelné.

Vyhněte se samotným polím. U pole na nejvyšší úrovni nemůžete připojit stránkování, součty ani kurzory, a jejich pozdější přidání znamená změnu každého volajícího.

Mají pole používat snake_case, nebo camelCase?

Vyberte jednu variantu, uplatněte ji v celém webu a nikdy je v jednom dokumentu JSON nemíchejte.

Styl Běžný ekosystém Příklad
camelCase JavaScript / TypeScript / Java userName, createdAt
snake_case Python / Ruby / Go (částečně) / databázové sloupce user_name, created_at
kebab-case Vzácné; klíče se spojovníkem se špatně přistupují user-name

Kritéria:

  1. Kdo tento JSON konzumuje: vyberte camelCase, když dominuje frontend, snake_case, když dominují datové pipeline a Python služby;
  2. Mapuje se přímo na databázové sloupce: snake_case ušetří převodní vrstvu při přímém průchodu strukturami tabulek;
  3. Kontinuita existujících systémů: ponechte, co existující API používá; konzistence vítězí nad správností.

Pokud skutečně musíte převádět, dělejte to na hranici (brána nebo vrstva požadavků frontendu), místo abyste rozptylovali volání toCamelCase po obchodním kódu.

Jaký formát mají používat časová pole?

Používejte řetězce RFC 3339 / ISO 8601 s offsetem časového pásma:

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

Klíčové body:

  • Uvádějte časové pásmo: 2026-10-30 11:37:57 bez pásma se v scénářích napříč pásmy nevyhnutelně rozbije;
  • Používejte Z nebo offset konzistentně: oba jsou správné, ale nemíchejte je v jednom API;
  • Vyhněte se číselným časovým razítkům: zda jde o sekundy nebo milisekundy a zda zahrnují přestupné sekundy, to musí každý tým znovu interpretovat. Když je potřeba kompaktnost, použijte celé číslo v milisekundách, ale jednotku zafixujte v dokumentaci.

Preferujte názvy polí s příponou At, například createdAt / updatedAt / deletedAt, aby bylo na první pohled zřejmé, že hodnota je bod v čase, nikoli trvání. Pro trvání použijte timeoutMs, ttlSeconds – jednotku dejte do názvu.

Má null být null, nebo vynechané pole?

Záleží na tom, zda „toto pole má význam“:

Sémantika Forma Poznámky
Pole existuje, ale zatím nemá hodnotu "name": null Explicitně říká „bylo zkontrolováno a je prázdné“
Pole s tímto zdrojem nesouvisí Vynechat Například běžný uživatel nemá mít vipExpireAt
Částečná aktualizace (PATCH) Vynechat = neměnit Zde jen null znamená „vymazat“

Třetí případ se nejčastěji přehlíží. V požadavku PATCH:

{ "nickname": null }

to znamená „vymaž přezdívku“, zatímco vynechání nickname z těla požadavku znamená „přezdívku neupravuj“. Pokud oba případy považujete za „vymazat“, uživatel, který si změní e-mail, si smaže přezdívku.

Ještě jednou: nepoužívejte undefined. Není to platná hodnota JSON a JSON.stringify klíč úplně zahodí, takže chování odpovídá „vynechat“, zatímco kód vypadá, že „byla explicitně předána prázdná hodnota“.

Jak mají být pojmenována booleovská pole?

Používejte prefixy is / has / can / should a vyhněte se negacím.

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

Tři ponaučení:

  1. Žádné negace: isDisabled: false nutí čtenáře jednou invertovat; isEnabled: true je přímější. Dvojitá negace (isNotDisabled) je pohroma;
  2. Nepoužívejte čísla pro booleany: "enabled": 1 nutí každého konzumenta jednou napsat === 1;
  3. Nepoužívejte řetězce: "enable": "true" je v JavaScriptu pravdivé a if (data.enable) je pravdivé i pro "false"; takové chyby se dohledávají extrémně těžko.

Mají výčtová pole používat čísla, nebo řetězce?

Používejte řetězce, pokud neexistuje jasné omezení úložiště nebo šířky pásma.

{ "status": "active" }

Ve srovnání s { "status": 1 } řetězce vítězí, protože:

  • Logy, odchyty paketů a databáze jsou přímo čitelné bez hledání v mapovací tabulce;
  • Přidání výčtové hodnoty nemění význam existujících hodnot (vložení hodnoty doprostřed číselného výčtu posune všechno);
  • Frontend nemusí udržovat mapování 1 → 'active' v synchronizaci.

Cenou je několik bajtů navíc, což je po kompresi téměř zanedbatelné.

Doprovodná praxe: pro hodnoty řetězcových výčtů používejte malá písmena + podtržítka (pending_review) a omezte je pomocí enum ve Schema, aby neplatné hodnoty byly odmítnuty už ve fázi validace.

2. Konfigurace a fixtures

Má konfigurace být JSON, YAML, nebo TOML?

Vyberte podle účelu, nikoli podle preference:

Formát Dobrý pro Špatný pro
JSON Programové čtení a zápis, striktně strukturovaná data, validace Schema Časté ruční úpravy (komentáře nejsou povoleny)
YAML Hluboce vnořená deployment konfigurace (K8s) Ručně psané odsazení je náchylné k chybám; yes/no se mění na booleany
TOML Plochá konfigurace závislostí a nástrojů (Cargo.toml, pyproject.toml) Špatná čitelnost u hlubokého vnoření
JSONC Konfigurace editoru, která potřebuje komentáře (tsconfig.json) Překračování hranic systémů

Častá chyba týmů je ukládat obchodní konfiguraci do YAML – implicitní převod typů v YAML (1.0 se stane číslem, NO se stane booleanem, 2026-10-30 se stane datem) produkuje v různých parserech různé výsledky.

Pokud konfigurace musí být přísně validována programem, zvolte JSON + Schema a komentáře dokumentujte polem _comment nebo samostatnou dokumentací.

Jak mají být organizovány testovací fixtures?

Organizujte je podle „jeden základ + minimální rozdíly“, nikoli kopírováním obrovského JSON pro každý testovací případ.

fixtures/
├─ users/
│  ├─ base.json              # Základní objekt uživatele
│  ├─ no-email.json          # Chybí jen e-mail
│  └─ invalid-age.json       # Jen věk je mimo rozsah
└─ fixtures.ts               # Pomocné funkce pro načtení a mělké sloučení
import baseUser from './users/base.json';

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

Tímto způsobem nový testovací případ musí napsat jen odlišná pole a změna základního pole automaticky aktualizuje každý případ.

Tři pravidla:

  1. Fixtures musí projít validací Schema: základní fixture sám má být platná data;
  2. Neexportujte skutečná produkční data: telefonní čísla, rodná čísla a adresy jsou všechny osobní údaje;
  3. Fixtures patří do verzovacího systému, ale musí zůstat malé: jeden soubor nad několik desítek KB si zaslouží prořezání nebo generování.

Jak snížit konflikty při slučování v souborech JSON?

JSON konfliktuje snadno: formátovač přetéká celý soubor najednou, takže dva lidé měnící po jednom řádku kolidují v celém souboru.

Čtyři taktiky:

  1. Sjednoťte formátování a přidejte ho do .editorconfig: konzistentní odsazení, konce řádků a koncový nový řádek zabraňují nesouvisejícím diffům;
  2. Seřaďte klíče: dohodněte abecední pořadí, aby nové pole nekolidovalo s místem vložení někoho jiného;
  3. Rozdělte soubory: rozbijte jeden obří config.json do několika souborů podle domény a konfliktní plocha se přirozeně zmenší;
  4. Vyhněte se commitům „přeformátuj všechno“: oddělte formátování od obchodních změn do dvou commitů, aby revize zůstala čitelná.

Pokud tým používá lock soubory (pnpm-lock.yaml, package-lock.json), neslučujte je ručně; regenerujte je:

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

Jak udržovat kompatibilitu verzí?

Princip je „přidávej, neměň, nemaž“.

Změna Kompatibilita Přístup
Přidání volitelného pole Kompatibilní Přidejte ho; konzumenti neznámá pole ignorují
Přidání povinného pole Nekompatibilní Zvyšte verzi, nebo poskytněte výchozí hodnotu
Odebrání pole Nekompatibilní Nejprve ho označte jako deprecated, sledujte volající, poté odeberte
Změna typu pole Nekompatibilní Přidejte nový název pole a během přechodu zapisujte do obou
Změna množiny výčtových hodnot Záleží Přidání hodnot je kompatibilní; odebrání ne

Podpůrné mechanismy:

  • Konzumenti používají „benevolentní parsování“: neznámá pole ignorují a chybějícím dodávají výchozí hodnoty;
  • Server chvíli drží stará pole a v odpovědi vrací hlavičku Deprecation jako nápovědu;
  • Ve Schema neoznačujte nová pole jako required a zastaralá pole označte deprecated: true.

Jak validovat kontrakty v CI?

Považujte Schema za součást kódu a validujte ho při každém commitu.

# Validujte vzorová data proti Schema (pomocí ajv-cli)
npx ajv-cli validate -s schemas/user.json -d "fixtures/users/*.json" --strict=false

V CI spouštějte tři věci:

  1. Platnost samotného Schema: zda je deklarace $schema platná a zda se $ref vyřeší;
  2. Zda fixtures odpovídají Schema: zajišťuje, že testovací data nedriftují;
  3. Zda je kontrakt zpětně kompatibilní: porovnejte s předchozí verzí a zkontrolujte odebraná pole nebo změněné typy.

Třetí bod potřebuje podporu nástrojů (například openapi-diff nebo json-schema-diff); bez nich alespoň porovnejte změny v polích required a množinách klíčů properties skriptem.

3. Rychlý přehled

Otázka Doporučení
Struktura odpovědi {"data":..., "meta":...}, s HTTP stavovými kódy pro chyby
Styl pojmenování camelCase (frontend-first) nebo snake_case (data-first), konzistentně v celém webu
Formát času Řetězce RFC 3339 s časovým pásmem, názvy polí končící At
Null null pro žádnou hodnotu, nesouvisející pole vynechat, vynechání v PATCH znamená „neměnit“
Pojmenování booleanů Prefixy is/has/can, žádné negace, žádné 0/1
Výčty Řetězce, malá písmena s podtržítky, enum ve Schema
Formát konfigurace JSON pro programovou validaci, TOML nebo JSONC pro ruční úpravy
Fixtures Jeden základ + překryvy s minimálním rozdílem, platné proti Schema
Konflikty slučování Sjednotit formátování, seřadit klíče, rozdělit soubory
Kompatibilita verzí Přidávej, neměň, nemaž; před odebráním označ jako deprecated