Pri vývoji rozsiahlych systémov (napr. podnikových aplikácií, zdieľaných softvérových balíkov, knižníc, ...), kde navzájom komunikuje viacero vrstiev a vývojárov, vzniká problém, ako riešiť vydávanie nových verzií kódu.
Pozrime sa na príklad situácie, keď chceme vytvoriť zdieľaný balík Composer pre komunitu vývojárov.
Pred vyriešením problému spätnej a doprednej kompatibility musíme zistiť, ako sledovať zmeny softvéru. V súčasnosti (2022) je najlepším spôsobom verzovania všetkých zmien systém Git. Úložisko softvéru možno zdieľať napríklad prostredníctvom GitHubu alebo GitLabu. Každá zmena softvéru má jedinečný identifikátor, ktorý identifikuje každú revíziu a opisuje, čo sa skutočne stalo.
Pri vývoji knižníc sa mi osvedčila nasledujúca stratégia:
Na začiatku vývoja sa vytvorí počiatočná revízia vo vetve master
(alebo main
), kde sa odovzdá základná štruktúra súborov.
Pre každú novú požiadavku sa vytvorí samostatná vetva z master, v ktorej sa bude pracovať. Keď je zmena pripravená, odošle sa požiadavka na zlúčenie do hlavnej zložky vo forme Pull request
. Nad požiadavkou sa vykoná kontrola kódu a ak je všetko v poriadku, zmena sa zlúči do hlavnej verzie.
Ak vetva obsahuje spätne nekompatibilnú zmenu (BC break, od Back Compatibility Break
), musí byť príslušne označená. Metóda označovania prestávok BC sa rozoberá v nasledujúcich kapitolách.
Produkčná verzia knižnice sa potom označí pomocou značiek, ktoré majú nasledujúcu štruktúru (na základe Semantic Versioning 2.0.0):
Číslo verzie zapisujeme vo formáte MAJOR.MINOR.PATCH
. Zvyšovanie čísel verzií sa vykonáva takto:
MAJOR
- ak ide o zmenu, ktorá nie je spätne kompatibilná s ostatnými (API)MINOR
- keď sa pridáva funkčnosť pri zachovaní spätnej kompatibilityPATCH
- keď je opravená chyba a zachovaná spätná kompatibilitaPomocou predbežných verzií a pridaním metadát je možné spresniť informácie. Napríklad: 1.0.0-alpha
, 1.0.1-beta+2
.
Viac informácií o sémantickom verziovaní nájdete na oficiálnej webovej lokalite: https://semver.org.
Pri navrhovaní softvéru by ste mali vždy myslieť na spätnú kompatibilitu (nové funkcie a zmeny musia byť kompatibilné so starým kódom) a v niektorých prípadoch aj na priamu kompatibilitu (súčasné funkcie musia byť kompatibilné s budúcimi zmenami rozhrania).
Správne zvládnutie oboch úloh je veľmi náročné. Nie vždy je možné vykonať zmenu bez porušenia kompatibility.
Pri vykonávaní zmien by ste mali vždy postupovať postupne a poskytnúť používateľom dostatok času na reakciu na zmeny.
Nasledujúce časti opisujú, ako o tom premýšľať.
Základným typom hrozby kompatibility je odstránenie alebo premenovanie funkcie, ktorá existovala v minulosti. Najčastejšie je to preto, že sa zmenili argumenty, ktoré funkcia prijíma, alebo ide o starú logiku, ktorá by sa mala novým spôsobom spracovať inak.
V prvej fáze by sa staré časti kódu mali označiť ako zastarané, ale nemali by sa nijako meniť.
V PHP je na to určená anotácia @deprecated
, ktorá by sa mala písať priamo nad metódy, funkcie, vlastnosti, premenné, konštanty a všeobecne všetok zastaraný kód.
Dobrým zvykom je tiež napísať dôvod, prečo je daná vec zastaraná a ako sa v budúcnosti zmení. Uveďte napríklad názov novej funkcie alebo spôsobu použitia.
Reálny príklad označenia kódu ako zastaraného: Konštanty budú odstránené, je lepšie použiť vstavaný Enum (BC break kvôli prechodu na novšiu verziu PHP):
class OrderNotification{/** @odstránené od 2022-05-24, použite enum OrderNotificationType */public constTYPE_EMAIL = 'e-mail',TYPE_SMS = 'text';
Anotácia @deprecated
spôsobí len tiché varovanie pre IDE (vývojový nástroj) a kompilačné nástroje. Nič sa tým neporušuje.
V druhej fáze nahradíme starú implementáciu novou, ale novú metódu použijeme v starej implementácii. Pomôže to zachovať kompatibilitu rozhrania bez toho, aby si to používateľ všimol.
Príklad: Metóda je zastaraná, pretože namiesto nej bola vytvorená nová statická služba. Keďže ju niekto môže používať, je len označená ako zastaraná a interne volá novú implementáciu. Vývojár môže vo všeobecnosti predpokladať, že metóda bude v budúcnosti úplne odstránená.
/** @odstránené od 2021-09-11 namiesto toho použite Ip::get(). */public static function userIp(): string{return Ip::get();}
Ak používate statickú analýzu, ako je PhpStan (odporúčame!), je dobré najprv prepísať anotácie PHPDoc a až potom zmeniť dátové typy. Statická analýza upozorní používateľa, že je niečo pokazené, ale runtime zostane nedotknutý.
Vo štvrtej fáze sa zavolá nová metóda a zároveň sa vyhodí chyba na úrovni note
. Aplikácia stále funguje, len sa postupne začne ukladať do systémového protokolu informácia, že funkcia je zastaraná a bude zmenená alebo odstránená. Na tento typ zmien budeme teraz aktívne upozorňovať. Vývojár uvidí chyby počas vývoja alebo kompilácie.
/** @zrušené od 2021-05-01, namiesto toho používajte UserMetaManager. */public function getMeta(int $userId, string $key): ?string{trigger_error(__METHOD__ . ': Táto metóda je zastaraná, namiesto nej použite UserMetaManager.');return $this->userMetaManager->get($userId, $key);}
Pred úplným odstránením metódy odporúčam vyhodiť jednu z fatálnych výnimiek. Je to obzvlášť dôležité, pretože aplikácia sa úplne zastaví a chybu nemožno ignorovať. Na rozdiel od úplného odstránenia kódu bude používateľ informovaný o tom, čo sa vlastne stalo, a môže chybu ľahko opraviť.
V poslednej fáze sa starý kód úplne odstráni. Ak niektorý používateľ neopravil závislosti, jeho aplikácia bude nefunkčná.
Závažné porušenia BC v citlivých oblastiach by sa mali vždy vykonať v nasledujúcej MAJOR
verzii a malo by sa na ne upozorniť aspoň o jednu MAJOR
verziu skôr vhodením upozornenia. Ak to neurobíte, aktualizácia knižnice bude veľmi náročná.
Jan Barášek Více o autorovi
Autor článku pracuje jako seniorní vývojář a software architekt v Praze. Navrhuje a spravuje velké webové aplikace, které znáte a používáte. Od roku 2009 nabral bohaté zkušenosti, které tímto webem předává dál.
Rád vám pomůžu:
Články píše Jan Barášek © 2009-2024 | Kontakt | Mapa webu
Status | Aktualizováno: ... | sk