Szemantikus Verziószámozás 2.0.0

Összefoglalás

Adott egy verziószám, MAJOR.MINOR.PATCH, növeljük:

  1. a MAJOR verziószámot, ha a változtatásaink eredményeként elveszítjük a kompatibilitást a korábbi verzióval,
  2. a MINOR verziószámot, ha olyan funkcionalitást adunk, ami megőrzi a visszafelé kompatibilitást, és
  3. a PATCH verziószámot, ha visszafelé kompatibilis hibajavításokat adunk hozzá.

További verziócímkéket és kiadással kapcsolatos metaadatokat a MAJOR.MINOR.PATCH formátum kiegészítéseként lehet még hozzáadni.

Bevezetés

A szoftvermenedzsment univerzumában létezik egy rettegett hely: a verziópokol. Minél nagyobbra nő a rendszered, minél több csomagot adsz hozzá, annál valószínűbb, hogy előbb-utóbb a kellős közepében találod magad.

Sok függőséggel rendelkező rendszerekben az új csomagverziók kiadása hamar rémálommá válhat. Ha túl szigorúak a függőségi specifikációid, fennáll a “verzió zár” veszélye (amikor már nem tudsz úgy frissíteni egy csomagot, hogy vele együtt az összes tőle függő csomagot ne kelljen frissíteni). Ugyanakkor, ha túl szabadon vannak ugyanezek megszabva, elkerülhetetlen, hogy idővel utolérjen a verzió-keveredés (amikor jobb kompatibilitást remélünk szoftver komponensek között, mint ami realisztikus). A verziópokol az, amikor e két helyzet közül az egyik vagy akár egyszerre mindkettő meggátol a projekt könnyed és rizikómentes előrevitelében.

A probléma megoldásaként javaslom ezen egyszerű szabályok és követelmények halmazát, melyek megszabják hogyan rendeljünk hozzá, illetve inkrementáljuk a verziószámokat. Ezen szabályok a már jól bevett gyakorlatokon alapulnak ― ám, nem szükségszerűen korlátozódnak rájuk -, mind zárt- és nyílt forráskódú szoftverekben. A rendszer első feltétele egy publikus API létrehozása. Ez egyaránt lehet dokumentáció formájában, vagy a programon keresztül meghatározva. Kulcsfontosságú, hogy ez az API átlátható es precíz legyen. Miután az API-t kiadtuk, minden változást a szoftverben verziószám növelésével kommunikálunk. Tekintsük az X.Y.Z(Major.Minor.Patch) verzióformátumot. Hibajavításoknál, melyek nem érintik az API-t, növeljük a patch verziót, visszafelé kompatibilis API kiegészítések/módosítások esetén növeljük a minor verziót, és visszafelé nem kompatibilis módosításoknál növeljük a major verziót. A rendszert “Szemantikus Verziószámozásnak” keresztelem. Ezen séma alapján, a verziószámok és ahogyan ezek változnak, jelentést hordoznak a mögöttes kódról és hogy mi változott egyik verzióról a másikra térve.

A Szemantikus Verziószámozás leírása (SemVer)

A kulcsszavak “MUSZÁJ” vagy “KÖTELEZŐEN” (“MUST”), “NEM LEHET” vagy “NEM SZABAD” (“MUST NOT”), “AJÁNLOTT” (“SHOULD”), “NEM AJÁNLOTT” (“SHOULD NOT”), “VÁLASZTHATÓ(AN)” (“OPTIONAL”) ebben a dokumentumban úgy vannak használva, mint ahogyan itt le vannak írva: RFC 2119.

  1. Szemantikus verziószámozást használó szoftvernek MUSZÁJ publikus API-t közzétennie. Ez az felület létezhet kizárólag dokumentáció formájában, vagy akár szoftverben meghatározva, de mindenféleképpen szükséges, hogy precíz és minden részletre kiterjedő legyen.

  2. Az általános verziószám formátuma MUSZÁJ, hogy az X.Y.Z legyen, ahol az X, Y és Z a természetes számok halmazába tartoznak, kezdő nullák nélkül. X a “major”, Y a “minor” és Z a “patch” verziószám. Mindhárom MUSZÁJ, hogy számtanilag helyesen növekedjen. Például: 1.9.0 -> 1.10.0 -> 1.11.0.

  3. Miután egy új csomagverzió forgalomba lett hozva, a csomag tartalma nem változhat. Bármilyen változás MUSZÁJ, hogy új verzióként legyen hozzáadva.

  4. A nulladik “major” verzió (0.Y.Z) korai fejlesztésekre alkalmazható. Bárhol várhatóak változások a szoftverben és a publikus API-tól nem elvárható, hogy tökéletesen stabil legyen.

  5. Az első verzió (1.0.0) definiálja a publikus API-t. Ezt követő verziószámok ennek az API-nak a változásától függnek.

  6. A “patch”, Z verziószámot (x.y.Z | x > 0) MUSZÁJ növelni, amikor kizárólag hátrafele kompatibilis hibajavításokat adunk a szoftverhez. Ilyen hibajavítás definíciója egy olyan belső változás, ami addig hibás viselkedést korrigál.

  7. A “minor”, Y verziószámot (x.Y.z | x > 0) MUSZÁJ növelni, amikor olyan új funkciókat adunk hozzá az API-hoz, ami a hátrafele kompatibilitást nem érinti. MUSZÁJ növelni, amikor korábbi funkciókat elavultként jelölünk meg. Növelni LEHET, amikor a új funkciók kerülnek a belső, privát kódbázisba. VÁLASZTHATÓAN tartalmazhat “patch” szintű változásokat is. A “patch” verziószám MUSZÁJ, hogy nullára csökkenjen amikor a “minor” verziószám növekszik.

  8. A “major”, X verziószámot (X.y.z | X > 0) MUSZÁJ növelni, amikor visszafelé inkompatibilis változások vannak hozzáadva a nyilvános API-hoz. VÁLASZTHATÓAN tartalmazhat “patch” és “minor” szintű változásokat is. A “patch” és “minor” verziószámok MUSZÁJ, hogy nullára csökkenjenek amikor a “major” verziószám növekszik.

  9. Egy kiadás előtti (“pre-release”) verzió VÁLASZTHATÓAN jelölhető egy kötőjelet követően pontokkal elválasztott azonosítókkal, közvetlenül a “patch” verzió után. Ezek az azonosítók MUSZÁJ, hogy kizárólag ASCII alfanumerikus karakterekből és kötőjelekből álljon. Azonosítók NEM LEHETNEK üresek. Számazonosítók NEM SZABAD, hogy nullával kezdődjenek. A kiadás előtti verziókhoz képest elsőbbséget élveznek a hozzátartozó normál verziók. Egy kiadás előtti verzió arra utal, hogy a verzió instabil és lehet, hogy nem elégíti ki a szándékozott kompatibilitási feltételeket amire a normál verzió utal. Például: 1.0.0-alpha, 1.0.0-alpha.1, 1.0.0-0.3.7, 1.0.0-x.7.z.92.

  10. Kiadással (“build”) kapcsolatos metaadatok VÁLASZTHATÓAN egy plusz jel hozzáadásával, majd pontokkal elválasztott azonosítókkal rögzíthetőek, közvetlenül a “patch” vagy kiadás előtti (“pre-release”) verzió után. Ezek az azonosítók MUSZÁJ, hogy kizárólag ASCII alfanumerikus karakterekből és kötőjelekből álljon. Azonosítók NEM LEHETNEK üresek. Számazonosítók NEM SZABAD, hogy nullával kezdődjenek. A kiadással kapcsolatos metaadatokat AJÁNLOTT figyelmen kívül hagyni a verziók elsőbbségének meghatározásakor. Ennélfogva két verzió, ami csupán kiadással kapcsolatos metaadatban különböznek egyenlő elsőbbséget élveznek. Például: 1.0.0-alpha+001, 1.0.0+20130313144700, 1.0.0-beta+exp.sha.5114f85.

  11. Az elsőbbség arra utal, hogy hogy viszonyulnak egymáshoz a verziók, amikor sorrendbe vannak téve. Az elsőbbséget MUSZÁJ a “major”, “minor”, “patch” és “pre-release” alapján számolni, ebben a sorrendben (kiadással kapcsolatos metaadatok, “build-metadata” nincsenek beleszámítva). Az elsőbbségi sorrendet az első eltérő azonosító határozza meg amikor balról jobbra olvassuk a verziószámot, tehát ebben a sorrendben: “major”, “minor” és “patch”, és a különbség számtanilag értelmezhető. Például: 1.0.0 < 2.0.0 < 2.1.0 < 2.1.1. Amikor a “major”, “minor” es “patch” verziószámok egyenlőek, a kiadás előtti (“pre-release”) verziónak kisebb elsőbbsége van. Például: 1.0.0-alpha < 1.0.0. Azonos normál verzióhoz tartozó, kiadás előtti (“pre-release”) verziók közötti elsőbbséget MUSZÁJ annak alapján meghatározni, hogy balról jobbra olvasva a ponttal elválasztott azonosítókat összehasonlítjuk karakterről karakterre. Számazonosítók számtanilag, alfanumerikus azonosítók ASCII alapján vannak sorrendbe rakva. Számazonosítóknak kisebb az elsőbbsége, mint a nem numerikus azonosítóknak. Amikor két “pre-release” verzió közötti különbség csak az, hogy az egyiknek az azonosítói pontosan megegyeznek a másikéval, de ezen felül még tartalmaz további azonosítókat is, akkor a több azonosítóval rendelkező verzió elsőbbséget élvez a másik felett. Például: 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0.

Miért használjam a Szemantikus Verziószámozást?

Ez nem egy új vagy forradalmi koncepció. Sőt, valószínűleg valami ehhez hasonló rendszert alkalmazol. A gond az az, hogy a ‘hasonló’ az nem elég. Egy hivatalos előíráshoz való szigorú ragaszkodás nélkül a verziószámok gyakorlatilag teljesen hasztalanná válnak a függőségek számontartásában. Másrészről viszont, megfelelő előírások és szabályok közlése mellett jelentősen egyszerűbb kommunikálni a szándékaidat a szoftvered felhasználóinak. Ha ezek a törekvések egyértelműek, rugalmasak (de nem túl rugalmasak), a függőségi előírások megfogalmazhatóvá válnak.

Egy egyszerű példa bemutatja, hogy a Szemantikus Verziószámozás hogyan tüntetheti el az Függőségek Számontartásának Poklát. Vegyünk egy “Tűzoltóautó” nevű csomagot. Szüksége van egy szemantikusan verziózott csomagra: a “Létrá”-ra. Amikor a Tűzoltóautót létrehozták, a Létra verziója 3.1.0. Mivel a Tűzoltóautó olyan funkcionalitást használ, ami a 3.1.0-ban lett bevezetve, nyugodtan meg lehet nevezni a függőséget: egy verziószám, ami nagyobb vagy egyenlő mint 3.1.0, de kisebb mint 4.0.0. Amikor a Létranak a 3.1.1-es, illetve 3.2.0-ás verziói elérhetővé válnak, bevezethetőek a Tűzoltóautó csomag menedzsment rendszerébe is anélkül, hogy a kompatibilitást veszélyeztetné.

Felelős szoftverfejlesztőként természetesen igazolni akarod majd, hogy a csomagfrissítések megegyeznek az elvártakkal. A világ egy kaotikus hely és ezzel szemben csak óvatosak lehetünk. Amit tehetsz az az, hogy legalább a saját szoftverfejlesztésed és kiadásodat szabályozza a Szemantikus Verziószámozás úgy, hogy az attól függő csomagok minimálisan legyenek érintve, időt és erőfeszítést spórolva.

Ha ez kívánatosnak hangzik, mindössze annyit kell tenned, hogy kijelentsd, hogy használod a Szemantikus Verziószámozást és kövesd a szabályait. Linkeld ezt a honlapot a csomagod README-jébe, hogy mások is megismerjék a szabályokat és részesülhessenek az előnyeiben.

GY.I.K.

Hogyan döntsek a revízióról a 0.y.z kezdeti fejlesztési fázisban?

A legegyszerűbb dolog amit tehetsz a kezdeti fejlesztési fázisban, hogy elkezded a 0.1.0 verzióval és a minor verziót inkrementálod minden egyes újabb kiadásnál.

Honnan tudom mikor jön el az 1.0.0 ideje?

Ha a szoftvered élesben lesz használva, akkor valószínűleg eljött az ideje az 1.0.0 verziónak. Ha van egy stabil API-d, amitől már felhasználók függnek, eljött az ideje az 1.0.0 verziónak. Ha tartasz a visszafelé kompatibilitástól, nagy a valószínűsége, hogy eljött az 1.0.0 ideje.

Nem ellenzi ez a gyors fejlesztést és a gyors iterációt?

A zéró major verzió a gyors fejlesztésről szól. Ha minden nap változtatod az API-t, még a 0.y.z verziónál kell hogy tarts vagy egy külön ágban kell hogy dolgozz a következő major verzión.

Ha a legkisebb visszafelé inkompatibilis változtatás is a publikus API-ban egy major verzió növelést kíván, nem fogok túl gyorsan a 42.0.0 verziónál kikötni?

Ez előrelátás és felelősségteljes fejlesztés kérdése. Inkompatibilis változtatást nem kéne könnyedén bevezetni olyan szoftverbe, ahol ez sok kód függvénye. A javítás során felmerülő költségek jelentősek lehetnek. Kiadásra szánt major verzió növelésénél végig kell gondolnod a változtatás hatásait és kiértékelned a benne foglalt költség/haszon arányt.

Túl sok munka dokumentálni az egész publikus API-t!

Professzionális fejlesztőként a te feladatod megfelelően dokumentálni az éles használatra kiadott szoftvert. A projekt hatékonyságának megtartása érdekében, egy másik rendkívül fontos feladat a szoftver komplexitásának kezelése, és azt nehéz kezelni, ha senki nem tudja, hogyan kell használni a szoftvert vagy milyen metódusokat biztonságos meghívni. Hosszútávon a Szemantikus verziószámozás, és a jól definiált publikus API-hoz való ragaszkodás meghozza az eredményét, mindenki és minden zökkenőmentesen fog haladni.

Mit tegyek, ha véletlenül kiadok egy visszafelé inkompatibilis változtatást minor verzióként?

Amint rájössz, hogy megszegted a Szemantikus Verziószámozás szabályait, javítsd a hibát és adj ki egy új minor verziót a javított hibával és a helyrehozott visszafelé kompatibilitással. Még ezen körülmények között is elfogadhatatlan, hogy módosíts kiadott verziót. Ha lehetséges, dokumentáld az érintett verziót és értesítsd a felhasználókat, hogy tisztában legyenek a verzió problémájával kapcsolatban.

Mit tegyek, ha frissítem a saját függőségeimet a publikus API változtatása nélkül?

Ez kompatibilisnek számít, mert nem érinti a publikus API-t. Azon szoftvernek, amelyre egyértelműen ugyanazok a függőségek állnak fent, mint a te csomagodra, saját függőségi specifikációval kell rendelkeznie, ezért az író észre fog venni bármilyen konfliktust. Annak megállapítása pedig, hogy a változtatás minor vagy major szintű, attól függ, hogy a függőségek módosítása bug javítása, avagy új funkció bevezetéséből eredendő. Az utóbbi esetben általában kód bővítésre lehet számítani, melynél egyértelmű, hogy minor szintű inkrementálásról beszélünk.

Mi a teendő, ha véletlenül megváltoztatom a publikus API-t oly módon, amely nem összeférhető a verzió szám változással (pl. nagy, major változást eszközölünk egy patch kiadásban)?

Dönts legjobb meglátásod szerint. Ha meglehetősen nagy a felhasználói kör, akiket drasztikusan befolyásolna a publikus API működésének degradálása, akkor a legjobb, amit tehetsz, hogy eszközölsz egy új major verzió kiadást, akkor is, ha a változás szigorúan egy patch kiadást kívánna. Tartsd észben, a szemantikus verziószámozás jelentést hordoz a verziószámok változásával. Ha a változások fontosak a felhasználóid számára, jelezd a verziószámokkal.

Hogyan kezeljem az elavult funkcionalitást?

Teljesen normális esemény a szoftverfejlesztésben, hogy egyes funkcionalitások elavulnak, sőt mi több, gyakran elvárt a szoftver fejlődésének érdekében. Amikor lecserélsz egy elavult részt a publikus API-dban, két dolgot kell tenned: (1) frissítsd a dokumentációt, hogy a felhasználók tudjanak a változásról, (2) eszközölj egy új minor kiadást az elavult résszel. Mielőtt teljesen eltörölnéd a funkcionalitást egy új, major kiadásban, kell lennie egy minor kiadásnak, ami még tartalmazza az elavult részt, hogy a felhasználók zökkenőmentesen át tudjanak állni az új API-ra.

Vannak-e megszorítások a verzió string hosszára nézve a semver szerint?

Nincs, de becsüljük meg észszerűen a hosszát. Például, egy 255 karakteres verzió string erős túlzás. Különböző rendszereknél, különböző verzió string hossz korlátokat alkalmazhatnak.

Infó

A Semantic Versioning specifikáció szerzője Tom Preston-Werner, a Gravatars feltalálója és a GitHub társalapítója.

Észrevételekért nyiss egy issue-t a GitHub-on.

Fordította: Rebeka Marton (szerző), Roland Kovács (szerző)

Licensz

Creative Commons ― CC BY 3.0