「轉(zhuǎn)」語義化版本 2.0.0

原文鏈接:https://semver.org/lang/zh-CN/

摘要

版本格式:主版本號(hào).次版本號(hào).修訂號(hào),版本號(hào)遞增規(guī)則如下:

  1. 主版本號(hào):當(dāng)你做了不兼容的 API 修改,
  2. 次版本號(hào):當(dāng)你做了向下兼容的功能性新增,
  3. 修訂號(hào):當(dāng)你做了向下兼容的問題修正。

先行版本號(hào)及版本編譯元數(shù)據(jù)可以加到“主版本號(hào).次版本號(hào).修訂號(hào)”的后面,作為延伸。

簡(jiǎn)介

在軟件管理的領(lǐng)域里存在著被稱作“依賴地獄”的死亡之谷,系統(tǒng)規(guī)模越大,加入的包越多,你就越有可能在未來的某一天發(fā)現(xiàn)自己已深陷絕望之中。

在依賴高的系統(tǒng)中發(fā)布新版本包可能很快會(huì)成為噩夢(mèng)。如果依賴關(guān)系過高,可能面臨版本控制被鎖死的風(fēng)險(xiǎn)(必須對(duì)每一個(gè)依賴包改版才能完成某次升級(jí))。而如果依賴關(guān)系過于松散,又將無法避免版本的混亂(假設(shè)兼容于未來的多個(gè)版本已超出了合理數(shù)量)。當(dāng)你專案的進(jìn)展因?yàn)榘姹疽蕾嚤绘i死或版本混亂變得不夠簡(jiǎn)便和可靠,就意味著你正處于依賴地獄之中。

作為這個(gè)問題的解決方案之一,我提議用一組簡(jiǎn)單的規(guī)則及條件來約束版本號(hào)的配置和增長(zhǎng)。這些規(guī)則是根據(jù)(但不局限于)已經(jīng)被各種封閉、開放源碼軟件所廣泛使用的慣例所設(shè)計(jì)。為了讓這套理論運(yùn)作,你必須先有定義好的公共 API 。這可以透過文件定義或代碼強(qiáng)制要求來實(shí)現(xiàn)。無論如何,這套 API 的清楚明了是十分重要的。一旦你定義了公共 API,你就可以透過修改相應(yīng)的版本號(hào)來向大家說明你的修改。考慮使用這樣的版本號(hào)格式:X.Y.Z (主版本號(hào).次版本號(hào).修訂號(hào))修復(fù)問題但不影響API 時(shí),遞增修訂號(hào);API 保持向下兼容的新增及修改時(shí),遞增次版本號(hào);進(jìn)行不向下兼容的修改時(shí),遞增主版本號(hào)。

我稱這套系統(tǒng)為“語義化的版本控制”,在這套約定下,版本號(hào)及其更新方式包含了相鄰版本間的底層代碼和修改內(nèi)容的信息。

語義化版本控制規(guī)范(SemVer)

以下關(guān)鍵詞 MUST、MUST NOT、REQUIRED、SHALL、SHALL NOT、SHOULD、SHOULD NOT、 RECOMMENDED、MAY、OPTIONAL 依照 RFC 2119 的敘述解讀。(譯注:為了保持語句順暢, 以下文件遇到的關(guān)鍵詞將依照整句語義進(jìn)行翻譯,在此先不進(jìn)行個(gè)別翻譯。)

  1. 使用語義化版本控制的軟件必須(MUST)定義公共 API。該 API 可以在代碼中被定義或出現(xiàn)于嚴(yán)謹(jǐn)?shù)奈募?nèi)。無論何種形式都應(yīng)該力求精確且完整。

  2. 標(biāo)準(zhǔn)的版本號(hào)必須(MUST)采用 X.Y.Z 的格式,其中 X、Y 和 Z 為非負(fù)的整數(shù),且禁止(MUST NOT)在數(shù)字前方補(bǔ)零。X 是主版本號(hào)、Y 是次版本號(hào)、而 Z 為修訂號(hào)。每個(gè)元素必須(MUST)以數(shù)值來遞增。例如:1.9.1 -> 1.10.0 -> 1.11.0。

  3. 標(biāo)記版本號(hào)的軟件發(fā)行后,禁止(MUST NOT)改變?cè)摪姹拒浖膬?nèi)容。任何修改都必須(MUST)以新版本發(fā)行。

  4. 主版本號(hào)為零(0.y.z)的軟件處于開發(fā)初始階段,一切都可能隨時(shí)被改變。這樣的公共 API 不應(yīng)該被視為穩(wěn)定版。

  5. 1.0.0 的版本號(hào)用于界定公共 API 的形成。這一版本之后所有的版本號(hào)更新都基于公共 API 及其修改內(nèi)容。

  6. 修訂號(hào) Z(x.y.Z | x > 0)必須(MUST)在只做了向下兼容的修正時(shí)才遞增。這里的修正指的是針對(duì)不正確結(jié)果而進(jìn)行的內(nèi)部修改。

  7. 次版本號(hào) Y(x.Y.z | x > 0)必須(MUST)在有向下兼容的新功能出現(xiàn)時(shí)遞增。在任何公共 API 的功能被標(biāo)記為棄用時(shí)也必須(MUST)遞增。也可以(MAY)在內(nèi)部程序有大量新功能或改進(jìn)被加入時(shí)遞增,其中可以(MAY)包括修訂級(jí)別的改變。每當(dāng)次版本號(hào)遞增時(shí),修訂號(hào)必須(MUST)歸零。

8.主版本號(hào) X(X.y.z | X > 0)必須(MUST)在有任何不兼容的修改被加入公共 API 時(shí)遞增。其中可以(MAY)包括次版本號(hào)及修訂級(jí)別的改變。每當(dāng)主版本號(hào)遞增時(shí),次版本號(hào)和修訂號(hào)必須(MUST)歸零。

9.先行版本號(hào)可以(MAY)被標(biāo)注在修訂版之后,先加上一個(gè)連接號(hào)再加上一連串以句點(diǎn)分隔的標(biāo)識(shí)符來修飾。標(biāo)識(shí)符必須(MUST)由 ASCII 字母數(shù)字和連接號(hào) [0-9A-Za-z-] 組成,且禁止(MUST NOT)留白。數(shù)字型的標(biāo)識(shí)符禁止(MUST NOT)在前方補(bǔ)零。先行版的優(yōu)先級(jí)低于相關(guān)聯(lián)的標(biāo)準(zhǔn)版本。被標(biāo)上先行版本號(hào)則表示這個(gè)版本并非穩(wěn)定而且可能無法滿足預(yù)期的兼容性需求。范例:1.0.0-alpha、1.0.0-alpha.1、1.0.0-0.3.7、1.0.0-x.7.z.92。

10.版本編譯元數(shù)據(jù)可以(MAY)被標(biāo)注在修訂版或先行版本號(hào)之后,先加上一個(gè)加號(hào)再加上一連串以句點(diǎn)分隔的標(biāo)識(shí)符來修飾。標(biāo)識(shí)符必須(MUST)由 ASCII 字母數(shù)字和連接號(hào) [0-9A-Za-z-] 組成,且禁止(MUST NOT)留白。當(dāng)判斷版本的優(yōu)先層級(jí)時(shí),版本編譯元數(shù)據(jù)可(SHOULD)被忽略。因此當(dāng)兩個(gè)版本只有在版本編譯元數(shù)據(jù)有差別時(shí),屬于相同的優(yōu)先層級(jí)。范例:1.0.0-alpha+001、1.0.0+20130313144700、1.0.0-beta+exp.sha.5114f85。

11.版本的優(yōu)先層級(jí)指的是不同版本在排序時(shí)如何比較。判斷優(yōu)先層級(jí)時(shí),必須(MUST)把版本依序拆分為主版本號(hào)、次版本號(hào)、修訂號(hào)及先行版本號(hào)后進(jìn)行比較(版本編譯元數(shù)據(jù)不在這份比較的列表中)。由左到右依序比較每個(gè)標(biāo)識(shí)符,第一個(gè)差異值用來決定優(yōu)先層級(jí):主版本號(hào)、次版本號(hào)及修訂號(hào)以數(shù)值比較,例如:1.0.0 < 2.0.0 < 2.1.0 < 2.1.1。當(dāng)主版本號(hào)、次版本號(hào)及修訂號(hào)都相同時(shí),改以優(yōu)先層級(jí)比較低的先行版本號(hào)決定。例如:1.0.0-alpha < 1.0.0。有相同主版本號(hào)、次版本號(hào)及修訂號(hào)的兩個(gè)先行版本號(hào),其優(yōu)先層級(jí)必須(MUST)透過由左到右的每個(gè)被句點(diǎn)分隔的標(biāo)識(shí)符來比較,直到找到一個(gè)差異值后決定:只有數(shù)字的標(biāo)識(shí)符以數(shù)值高低比較,有字母或連接號(hào)時(shí)則逐字以 ASCII 的排序來比較。數(shù)字的標(biāo)識(shí)符比非數(shù)字的標(biāo)識(shí)符優(yōu)先層級(jí)低。若開頭的標(biāo)識(shí)符都相同時(shí),欄位比較多的先行版本號(hào)優(yōu)先層級(jí)比較高。范例: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。

為什么要使用語義化的版本控制?

這并不是一個(gè)新的或者革命性的想法。實(shí)際上,你可能已經(jīng)在做一些近似的事情了。問題在于只是“近似”還不夠。如果沒有某個(gè)正式的規(guī)范可循,版本號(hào)對(duì)于依賴的管理并無實(shí)質(zhì)意義。將上述的想法命名并給予清楚的定義,讓你對(duì)軟件使用者傳達(dá)意向變得容易。一旦這些意向變得清楚,彈性(但又不會(huì)太彈性)的依賴規(guī)范就能達(dá)成。

舉個(gè)簡(jiǎn)單的例子就可以展示語義化的版本控制如何讓依賴地獄成為過去。假設(shè)有個(gè)名為“救火車”的函式庫,它需要另一個(gè)名為“梯子”并已經(jīng)有使用語義化版本控制的包。當(dāng)救火車創(chuàng)建時(shí),梯子的版本號(hào)為 3.1.0。因?yàn)榫然疖囀褂昧艘恍┌姹?3.1.0 所新增的功能, 你可以放心地指定依賴于梯子的版本號(hào)大等于 3.1.0 但小于 4.0.0。這樣,當(dāng)梯子版本 3.1.1 和 3.2.0 發(fā)布時(shí),你可以將直接它們納入你的包管理系統(tǒng),因?yàn)樗鼈兡芘c原有依賴的軟件兼容。

作為一位負(fù)責(zé)任的開發(fā)者,你理當(dāng)確保每次包升級(jí)的運(yùn)作與版本號(hào)的表述一致。現(xiàn)實(shí)世界是復(fù)雜的,我們除了提高警覺外能做的不多。你所能做的就是讓語義化的版本控制為你提供一個(gè)健全的方式來發(fā)行以及升級(jí)包,而無需推出新的依賴包,節(jié)省你的時(shí)間及煩惱。

如果你對(duì)此認(rèn)同,希望立即開始使用語義化版本控制,你只需聲明你的函式庫正在使用它并遵循這些規(guī)則就可以了。請(qǐng)?jiān)谀愕?README 文件中保留此頁連結(jié),讓別人也知道這些規(guī)則并從中受益。

FAQ

在 0.y.z 初始開發(fā)階段,我該如何進(jìn)行版本控制?

最簡(jiǎn)單的做法是以 0.1.0 作為你的初始化開發(fā)版本,并在后續(xù)的每次發(fā)行時(shí)遞增次版本號(hào)。

如何判斷發(fā)布 1.0.0 版本的時(shí)機(jī)?

當(dāng)你的軟件被用于正式環(huán)境,它應(yīng)該已經(jīng)達(dá)到了 1.0.0 版。如果你已經(jīng)有個(gè)穩(wěn)定的 API 被使用者依賴,也會(huì)是 1.0.0 版。如果你很擔(dān)心向下兼容的問題,也應(yīng)該算是 1.0.0 版了。

這不會(huì)阻礙快速開發(fā)和迭代嗎?

主版本號(hào)為零的時(shí)候就是為了做快速開發(fā)。如果你每天都在改變 API,那么你應(yīng)該仍在主版本號(hào)為零的階段(0.y.z),或是正在下個(gè)主版本的獨(dú)立開發(fā)分支中。

對(duì)于公共 API,若即使是最小但不向下兼容的改變都需要產(chǎn)生新的主版本號(hào),豈不是很快就達(dá)到 42.0.0 版?

這是開發(fā)的責(zé)任感和前瞻性的問題。不兼容的改變不應(yīng)該輕易被加入到有許多依賴代碼的軟件中。升級(jí)所付出的代價(jià)可能是巨大的。要遞增主版本號(hào)來發(fā)行不兼容的改版,意味著你必須為這些改變所帶來的影響深思熟慮,并且評(píng)估所涉及的成本及效益比。

為整個(gè)公共 API 寫文件太費(fèi)事了!

為供他人使用的軟件編寫適當(dāng)?shù)奈募悄阕鳛橐幻麑I(yè)開發(fā)者應(yīng)盡的職責(zé)。保持專案高效一個(gè)非常重要的部份是掌控軟件的復(fù)雜度,如果沒有人知道如何使用你的軟件或不知道哪些函數(shù)的調(diào)用是可靠的,要掌控復(fù)雜度會(huì)是困難的。長(zhǎng)遠(yuǎn)來看,使用語義化版本控制以及對(duì)于公共 API 有良好規(guī)范的堅(jiān)持,可以讓每個(gè)人及每件事都運(yùn)行順暢。

萬一不小心把一個(gè)不兼容的改版當(dāng)成了次版本號(hào)發(fā)行了該怎么辦?

一旦發(fā)現(xiàn)自己破壞了語義化版本控制的規(guī)范,就要修正這個(gè)問題,并發(fā)行一個(gè)新的次版本號(hào)來更正這個(gè)問題并且恢復(fù)向下兼容。即使是這種情況,也不能去修改已發(fā)行的版本。可以的話,將有問題的版本號(hào)記錄到文件中,告訴使用者問題所在,讓他們能夠意識(shí)到這是有問題的版本。

如果我更新了自己的依賴但沒有改變公共 API 該怎么辦?

由于沒有影響到公共 API,這可以被認(rèn)定是兼容的。若某個(gè)軟件和你的包有共同依賴,則它會(huì)有自己的依賴規(guī)范,作者也會(huì)告知可能的沖突。要判斷改版是屬于修訂等級(jí)或是次版等級(jí),是依據(jù)你更新的依賴關(guān)系是為了修復(fù)問題或是加入新功能。對(duì)于后者,我經(jīng)常會(huì)預(yù)期伴隨著更多的代碼,這顯然會(huì)是一個(gè)次版本號(hào)級(jí)別的遞增。

如果我變更了公共 API 但無意中未遵循版本號(hào)的改動(dòng)怎么辦呢?(意即在修訂等級(jí)的發(fā)布中,誤將重大且不兼容的改變加到代碼之中)

自行做最佳的判斷。如果你有龐大的使用者群在依照公共 API 的意圖而變更行為后會(huì)大受影響,那么最好做一次主版本的發(fā)布,即使嚴(yán)格來說這個(gè)修復(fù)僅是修訂等級(jí)的發(fā)布。記住, 語義化的版本控制就是透過版本號(hào)的改變來傳達(dá)意義。若這些改變對(duì)你的使用者是重要的,那就透過版本號(hào)來向他們說明。

我該如何處理即將棄用的功能?

棄用現(xiàn)存的功能是軟件開發(fā)中的家常便飯,也通常是向前發(fā)展所必須的。當(dāng)你棄用部份公共 API 時(shí),你應(yīng)該做兩件事:(1)更新你的文件讓使用者知道這個(gè)改變,(2)在適當(dāng)?shù)臅r(shí)機(jī)將棄用的功能透過新的次版本號(hào)發(fā)布。在新的主版本完全移除棄用功能前,至少要有一個(gè)次版本包含這個(gè)棄用信息,這樣使用者才能平順地轉(zhuǎn)移到新版 API。

語義化版本對(duì)于版本的字串長(zhǎng)度是否有限制呢?

沒有,請(qǐng)自行做適當(dāng)?shù)呐袛唷Ee例來說,長(zhǎng)到 255 個(gè)字元的版本已過度夸張。再者,特定的系統(tǒng)對(duì)于字串長(zhǎng)度可能會(huì)有他們自己的限制。

關(guān)于

語義化版本控制的規(guī)范是由 Gravatars 創(chuàng)辦者兼 GitHub 共同創(chuàng)辦者 Tom Preston-Werner 所建立。

如果您有任何建議,請(qǐng)到 GitHub 上提出您的問題

許可證

知識(shí)共享 署名 3.0 (CC BY 3.0)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,786評(píng)論 6 534
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,656評(píng)論 3 419
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,697評(píng)論 0 379
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,098評(píng)論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,855評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,254評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,322評(píng)論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,473評(píng)論 0 289
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,014評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,833評(píng)論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,016評(píng)論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,568評(píng)論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,273評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,680評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,946評(píng)論 1 288
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,730評(píng)論 3 393
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,006評(píng)論 2 374