Breaking changes: co se počítá a jak je vydat
8 min čtení aktualizováno
Breaking change je změna, kterou by korektně napsaný volající nedokázal přežít. Definice záleží, protože většina sporů o to, zda se něco «počítá», jsou ve skutečnosti spory o to, kdo to držel špatně. Pokud volající následoval vaši dokumentaci a vaše změna způsobila, že jeho kód přestal fungovat, změna byla breaking. Co jste zamýšleli, s tím nemá nic společného.
To je celý test. Zbytek tohoto článku je to, co z něj vyplývá: co jím neprojde, co projde, jak zachytit selhání dřív, než se změna sloučí, a co dělat, jakmile víte, že takovou změnu vydáváte.
Co se počítá jako breaking change?
Aplikujte test na volajícího, ne na diff. Změna je breaking, když volající, který se spoléhal pouze na dokumentované chování, musí změnit svůj kód, konfiguraci nebo data, aby dál fungoval. Odstranění pole, přejmenování endpointu, zpřísnění validace, změna výchozí hodnoty a změna typu hodnoty se všechny kvalifikují. Přidání volitelného pole ne. Oprava chyby obvykle ne, s jednou důležitou výjimkou níže.
| Změna | Breaking? | Proč |
|---|---|---|
| Odstranění nebo přejmenování pole, endpointu, flagu nebo možnosti | Ano | Korektní volající na to odkazují |
| Přidání volitelného pole nebo nového endpointu | Ne | Existující volání se nemění |
| Změna volitelného vstupu na povinný | Ano | Volání, která ho vynechávala, teď selhávají |
| Zpřísnění dříve přijímané validace | Ano | Vstupy, které fungovaly, jsou nyní odmítány |
| Změna výchozí hodnoty | Ano | Volající, kteří ji nenastavili, dostávají nové chování |
| Změna typu (řetězec na číslo, jednotlivá hodnota na pole) | Ano | Parsery napsané pro dokumentovaný typ selhávají |
| Přeuspořádání klíčů objektu | Ne | Pokud jste pořadí nedokumentovali |
| Oprava chyby, na kterou se volající spoléhali | V praxi ano | Viz sekce o náhodných smlouvách |
| Zvýšení limitu rychlosti nebo velikosti | Ne | Nic, co fungovalo, nepřestává fungovat |
| Snížení limitu rychlosti nebo velikosti | Ano | Provoz, který byl v pořádku, je nyní omezen |
| Změna formulace chybové zprávy | Záleží | Breaking, pokud jste to dokumentovali nebo volající na to porovnávají |
Co není breaking change?
Změna není breaking, když každé volání, které dřív fungovalo, funguje dál beze změny a znamená totéž. Přidání nového endpointu, přidání volitelného parametru požadavku, přidání pole do odpovědi, změna povinného vstupu na volitelný, zvýšení limitu a vylepšení chybové zprávy, na kterou nikdo neporovnává, všechno test splní. Takové aditivní změny mohou vyjít v minor vydání s běžným záznamem v changelogu.
Aditivní změny přesto rozbíjejí volající ve třech situacích. Klient, jehož deserializér odmítá neznámá pole, selže na prvním novém poli odpovědi, proto včas zdokumentujte, že volající mají ignorovat pole, která neznají. Nová hodnota enumu rozbije každého volajícího s vyčerpávajícím switchem (víc o tom níže). A odpověď, která naroste, může volajícího přetlačit přes limit velikosti, timeout nebo šířku sloupce, o kterých nikdy nemusel přemýšlet.
Čtyři řádky tabulky si zaslouží bližší pohled, protože právě tam vznikají neshody.
Čtyři breaking changes, které týmy přehlížejí
Náhodné smlouvy. Pokud vaše API tři roky vracelo stejné nedokumentované pole, volající na tom postavil. Hyrumův zákon je krátká verze: při dostatečném počtu uživatelů bude na každém pozorovatelném chování vašeho systému někdo záviset. Proto «byla to oprava chyby» není obhajoba. Oprava může být korektní a přesto breaking. Vydejte ji jako takovou.
Změny chování bez změny schématu. Pole je stále tam, typ je stejný, a hodnota nyní znamená
něco jiného. status, který byl active nebo inactive, a nyní vrací i suspended, rozbije
každého volajícího s vyčerpávajícím switchem. Timestamp, který přechází z místního času na UTC,
rozbije každého, kdo si dokumentaci nepřečetl dvakrát. Nic v diffu souboru OpenAPI to neukazuje.
Zpřísněná validace. Začnete odmítat e-maily bez TLD, nebo mezery na konci, nebo jména delší než 80 znaků. Každý volající, který posílal přesně to, teď dostane 400 na požadavek, který fungoval minulý týden. Změny validace jsou nejčastěji vydávané jako oprava «zpřísnění».
Změněné výchozí hodnoty. Nikdo, kdo hodnotu nastavil explicitně, si ničeho nevšimne. Všichni, kdo to neudělali, což je většina volajících, dostávají nové chování, aniž by změnili řádek. Změněná výchozí hodnota rozbije většinu vašich uživatelů přesně proto, že nikdy toto nastavení neviděli.
Jak odhalit breaking change dřív, než vyjde?
Porovnejte kontrakt v pull requestu s kontraktem na hlavní větvi, v CI, a při breaking rozdílu nechte build selhat. Nástroje pro porovnání schémat existují pro většinu formátů rozhraní a každý zná pravidla breaking změn svého formátu:
| Rozhraní | Nástroj | Co porovnává |
|---|---|---|
| REST (OpenAPI) | oasdiff | Dvě specifikace OpenAPI, s reportem breaking changes |
| gRPC (Protobuf) | buf breaking | Soubory .proto, na úrovni wire nebo zdrojového kódu |
| GraphQL | GraphQL Inspector | Dvě schémata, s označením breaking a nebezpečných změn |
| Rust crates | cargo-semver-checks | Veřejné API proti poslední publikované verzi |
| Balíčky TypeScript | API Extractor | Commitnutý report veřejného API balíčku |
Tyto nástroje spolehlivě zachytí odstraněná pole, přejmenované operace a změněné typy. První dva ze čtyř typů výše, náhodnou smlouvu a změnu chování, vidět nemohou, protože ani jedno se ve schématu neprojeví. Nástrojem zastavte ty zjevné a pro zbytek použijte revizní otázku «mohl by to korektní volající zaznamenat?». Stejná úloha v CI je přirozené místo, kde vyžadovat záznam v changelogu, jak popisuje článek vynucení záznamů changelogu v CI, a změny API v gRPC a Protobuf probírá případy na úrovni wire.
Jak označit breaking change v commitu?
S Conventional Commits se breaking change
označuje vykřičníkem ! před dvojtečkou (feat(api)!: remove the legacy export endpoint) nebo
patičkou, která začíná BREAKING CHANGE: a pokračuje popisem. Obojí odpovídá major verzi.
Patičku pište jako první návrh záznamu v changelogu: uveďte, koho se to týká a co musí udělat.
O tom, jak daleko vás konvence dovede, pojednává
Conventional commits a changelog.
Stejné pravidlo platí pro knihovny. Odstraněná veřejná funkce, zúžený typ parametru nebo změněná návratová hodnota jsou podle sémantického verzování major verze. Knihovny se jím ne vždy řídí: studie 119 879 upgradů na Maven Central zjistila, že 16,6 % porušilo sémantické verzování, přesto bylo zasaženo jen 7,9 % klientských projektů, protože většina těchto změn se dotkla kódu, který žádný klient nevolal. Rozbití se měří u volajícího.
Jak se vydává breaking change?
Vydáváte ho otevřeně, s datem, s cestou. Kroky níže jsou v pořadí, a poslední je ten, který většina týmů přeskočí: říct lidem, kterých se to týkalo, že to, na co čekali, se nyní stalo.
- Rozhodněte, zda to je jeden. Použijte test výše, ne diff. Pokud se dva inženýři neshodnou, je to breaking; neshoda je důkazem, že volající se mohl rozumně spoléhat na staré chování.
- Verzujte to. Podle sémantického verzování je breaking change major verze. Pokud provozujete datované nebo verzované API, jde to do nové verze, a stará dál funguje do oznámeného data. Pokud nemůžete verzovat, nevydáváte breaking change, vydáváte výpadek se záznamem changelogu. Které schéma nese verzi, je téma nejlepších praktik verzování API.
- Napište záznam předtím, než se kód sloučí. Záznam má pevnou formu: co se mění, koho se to týká, co musí udělat, a do kdy. Pokud nemůžete vyplnit všechny čtyři, změna není připravena. Šablona release notes klade tyto záznamy jako první, s datem místo čísla verze, přesně proto.
- Dejte termín, ne číslo vydání. «Odstraněno v v5» nic neznamená pro toho, kdo nesleduje vaše vydání. «Přestává fungovat 1. listopadu 2026» znamená totéž pro všechny.
- Poskytněte migraci. Ukázku kódu starého volání vedle nového. Pokud je změna přejmenování, uveďte obě jména ve stejné větě. Pokud je to odstraněné pole, řekněte, kam data šla.
- Oznamte to všude, kde bylo staré chování dokumentováno. Changelog, stránku dokumentace, která endpoint popisuje, release notes SDK, a hlavičku deprekace v odpovědi, pokud ji máte. Oznámené na jednom místě je oznámené lidem, kteří se tam náhodou podívali.
- Uzavřete smyčku. Pokud o změnu zákaznice požádala, nebo nahlásila chybu, která k ní vedla, řekněte jí, kdy je vydána. To je krok, který to promění z něčeho uděláného vašim uživatelům na něco uděláného spolu s nimi.
Jak vypadá dobrý záznam o breaking change?
Dobrý záznam pojmenuje postiženého volajícího v první řádce, uvede datum, a zahrnuje opravu. Zde jeden pro případ zpřísněné validace, ve formě, kterou používáme:
E-mailové adresy bez domény jsou odmítány od 1. listopadu 2026.
POST /usersaPATCH /users/:idv současnosti přijímají hodnotyalice@localhost. Od 1. listopadu tyto vrací400 invalid_email. Týká se jakékoli integrace, která vytváří uživatele z interních adresářů. Migrace: pošlete plně kvalifikovanou adresu, nebo pole vynechte a nastavte ho později. Žádná změna není potřeba, pokud vaše adresy už mají doménu, což platí pro 99,4 % účtů vytvořených letos.
Kde má toto oznámení místo, a co dalšího by ho mělo doprovázet, řeší API changelog.
Procento na konci není dekorace. Říká čtenářce, zda se má bát, což je otázka, se kterou záznam otevřela.
Proč se jim jednoduše nevyhnout?
Protože alternativa je horší. API, které nikdy nic nerozbije, hromadí každou chybu, kterou kdy udělalo: špatně pojmenované pole, špatnou výchozí hodnotu, timestamp v místním čase. Každá z nich je daň pro každého nového volajícího navždy, aby chránila volající, kteří mohli migrovat za odpoledne. Týmy s nejlepší pověstí stability rozbíjejí věci zřídka, podle harmonogramu, s cestou migrace a varováním, které dosáhlo lidí, pro které bylo určeno.
Mechanika tohoto varování je téma doprovodného článku o deprekaci API. Záznam, který to oznamuje, se sestavuje stejným způsobem jako jakýkoli jiný záznam v kanálu changelogu: ze sloučeného pull requestu, zadrženého pro člověka, pak zveřejněného tam, kde postižení volající už čtou.
FAQ
Jaký je rozdíl mezi breaking a non-breaking změnou? Breaking change nutí korektního volajícího změnit kód, konfiguraci nebo data, aby dál fungoval. Non-breaking změna ponechá každé existující volání funkční se stejným významem, proto jsou přidání obvykle bezpečná a odstranění, přejmenování a zpřísněná pravidla obvykle ne.
Počítá se přidání povinného pole? Ano. Každé existující volání ho vynechává, takže každé existující volání teď selže. Přidejte ho jako volitelné s rozumnou výchozí hodnotou, nebo verzujte endpoint.
Počítá se oprava chyby? Může být. Pokud se volající spoléhali na chybové chování, jeho oprava je rozbije, ať dokumentace říkala cokoli. Zacházejte s jakoukoli opravou, která mění pozorovatelný výstup, jako s breaking, pokud nemůžete ukázat, že se na ni nikdo nespoléhal.
Platí sémantické verzování pro webové API? Pravidlo ano: breaking changes dostávají novou major verzi, a stará dál funguje po oznámené období. Číslo často žije v URL nebo hlavičce data místo ve verzi balíčku.
Kolik předstihu je dostatek? Dost na to, aby volající našel oznámení a udělal práci. Devadesát dní je běžné minimum pro veřejná API; déle pro cokoli používané v kódu, který se odesílá koncovým uživatelům a nelze ho vzdáleně aktualizovat.
Technická tvrzení v tomto článku nikdo nezávisle neověřil. Pokud tu něco nesedí, dej nám vědět a opravíme to.