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í.

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:
- Kdo tento JSON konzumuje: vyberte camelCase, když dominuje frontend, snake_case, když dominují datové pipeline a Python služby;
- Mapuje se přímo na databázové sloupce: snake_case ušetří převodní vrstvu při přímém průchodu strukturami tabulek;
- 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:57bez pásma se v scénářích napříč pásmy nevyhnutelně rozbije; - Používejte
Znebo 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í:
- Žádné negace:
isDisabled: falsenutí čtenáře jednou invertovat;isEnabled: trueje přímější. Dvojitá negace (isNotDisabled) je pohroma; - Nepoužívejte čísla pro booleany:
"enabled": 1nutí každého konzumenta jednou napsat=== 1; - Nepoužívejte řetězce:
"enable": "true"je v JavaScriptu pravdivé aif (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:
- Fixtures musí projít validací Schema: základní fixture sám má být platná data;
- Neexportujte skutečná produkční data: telefonní čísla, rodná čísla a adresy jsou všechny osobní údaje;
- 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:
- 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; - Seřaďte klíče: dohodněte abecední pořadí, aby nové pole nekolidovalo s místem vložení někoho jiného;
- Rozdělte soubory: rozbijte jeden obří
config.jsondo několika souborů podle domény a konfliktní plocha se přirozeně zmenší; - 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
Deprecationjako nápovědu; - Ve Schema neoznačujte nová pole jako
requireda zastaralá pole označtedeprecated: 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:
- Platnost samotného Schema: zda je deklarace
$schemaplatná a zda se$refvyřeší; - Zda fixtures odpovídají Schema: zajišťuje, že testovací data nedriftují;
- 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 |