惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
WordPress大学
WordPress大学
宝玉的分享
宝玉的分享
人人都是产品经理
人人都是产品经理
博客园 - 聂微东
IT之家
IT之家
V
V2EX
Jina AI
Jina AI
V
Visual Studio Blog
有赞技术团队
有赞技术团队
博客园 - 司徒正美
博客园 - 叶小钗
The Cloudflare Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
小众软件
小众软件
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - 三生石上(FineUI控件)
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Google DeepMind News
Google DeepMind News
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
腾讯CDC
Google Online Security Blog
Google Online Security Blog
博客园 - 【当耐特】
Apple Machine Learning Research
Apple Machine Learning Research
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
N
News and Events Feed by Topic
N
News and Events Feed by Topic
The Last Watchdog
The Last Watchdog
W
WeLiveSecurity
月光博客
月光博客
Security Archives - TechRepublic
Security Archives - TechRepublic
Webroot Blog
Webroot Blog
SecWiki News
SecWiki News
博客园_首页
罗磊的独立博客
量子位
Latest news
Latest news
I
Intezer
V
Vulnerabilities – Threatpost
A
Arctic Wolf
Last Week in AI
Last Week in AI
Recent Commits to openclaw:main
Recent Commits to openclaw:main
S
SegmentFault 最新的问题
S
Security Affairs
阮一峰的网络日志
阮一峰的网络日志
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
酷 壳 – CoolShell
酷 壳 – CoolShell
P
Palo Alto Networks Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
N
News | PayPal Newsroom

囧克斯

奔四了,写给40岁的自己 有序退网 我和 Vue.js 的十年 我参与《代码之外 Beyond Code》的故事 博客站迁移至 VitePress 的备忘 写给我的奶奶 中文格式化小工具 zhlint 及其开发心得 vue-mark-display:用 markdown 语法轻松撰写幻灯片 我对技术会议的一些看法 VueConf Hangzhou 见闻 第四届 CSSConf CN 见闻 [译]Web 表单的未来 [译]苹果正在做一些他们的程序员明摆着不想要的东西 [译]为什么我不会无偿加班且你也不应该 [译]如果管理是唯一可走的路,那就完蛋了 [译]如何撰写 Git 提交信息 [译]C 程序的原则 Vue 2.0 来了! Weex 近 4 个月的开源之路 Weex 在 JS Runtime 内的多实例管理 我理解的 SPA 我理解的 Flux 架构 【整理】Vue 2.0 自 beta 1 到 beta 4 以来的主要更新 通过一张图走进 Vue 2.0 Code Review for Vue 2.0 Preview Vue 2.0 发布啦! 务实的小而美 Vue.js 1.0.0 发布了! [译]如何成为一名卓越的前端工程师 手机淘宝前端的图片相关工作流程梳理 [译]如何让办公室政治最小化 Vue.js 源码学习笔记 从原型到发布——“团队时间线” 1.0 开发心得 Vue + webpack 项目实践 用 Koa 写服务体验 webcomponents 笔记 之 配置管理 小秀个人的13~14年摄影作品 (共19张) 由今年D2前端论坛想到的 [译]CSS命名神马的真心难 [译]Git 分支的最佳实践 [译]撰写可测试的 JavaScript [译]通过HTML5 Canvas API调节图像的亮度和颜色 [译]JavaScript V8性能小贴士 [译]视觉差,走起! [译]Chrome开发者工具中评估性能的五大新特性 精气神儿 细节无微不至,彩屏让人又爱又恨——新老“神机”大对决:Nokia 1050 vs Nokia 1202 秦升拿到红牌之后…… 用Sass重新整理自己的博客主题样式 Connect中间件使用手册 巧用 RequireJS Optimizer 给传统的前端项目打包 编辑器小调查结果 [译]JSLint 文档 烟火——写给蛇年的傲游和我 小秀个人的全年摄影作品 (共15张) 2012年终毫无正能量的总结 标签?ID?还是CLASS? 微创新=伪创新 HTML5峰会归来 分享bookmarklet一则:随意阅读 国际羽联和中国队之间的恶性循环 ZeroClipboard 学习笔记 “思考人生” 听杨东杰弹吉他 学习精髓 网站装修笔记20120426 网站装修笔记20120414 网站装修笔记20120406 分享Typecho插件:百度统计助手 我的得奖感言 网站装修笔记20120331 用CSS3制作尖角标签按钮样式 分享Typecho插件:Markdown 解析器 + 编辑器 分享Typecho皮肤:我的字很大 汇总自己过去的一些HTML5科普文章 html5中的消息通信简介 + 我的新网站开张 网站装修计划 Typed Arrays 是神马? 文明看球 HTML5中的文件处理 之 File Writer API HTML5中的文件处理 之 File API 独生子女、互相等和不耐烦 IndexedDB技术简介(四) IndexedDB技术简介(三) IndexedDB技术简介(二) IndexedDB技术简介(一) 把博客的字体进一步调大,同时去掉了侧边栏 “模仿别人是为了找到自己” 写给我未婚妻的2011年 写给自己的2011年 手把手教你入门EaselJS做HTML5动画 “吃了吧,不吃就浪费了” 写给傲游的2011年 所谓专业 写给HTML5的2011年 对HTML5中LocalStorage的一些使用建议 回味一下我2011年看到的最过瘾的5场足球比赛 浅浅浅谈开饭店被顾客吃出“异物”的用户体验 前端的本职 继续对Mac穷追猛打 之 修改原生按钮文字大小的技巧
[译]语义化版本管理
勾三股四 · 2013-12-01 · via 囧克斯

译自:语义化版本管理 2.0.0

摘要 ​

对于一个给定的版本号 MAJOR.MINOR.PATCH (主、次、补丁),其变化的规律是:

  1. MAJOR version (主版本) 会在 API 发生不可向下兼容的改变时增大。
  2. MINOR version (次版本) 会在有向下兼容的新功能加入时增大。
  3. PATCH version (补丁版本) 会在bug以向下兼容的方式被修复时增大。

我们还可以根据预发布、构建元数据 (build metadata) 的实际需求,在 MAJOR.MINOR.PATCH 格式之上扩展出额外的标记。

介绍 ​

在软件管理领域,存在一个叫做“dependency hell (依赖地狱)”的坑。随着系统越变越大,你集成了越多的软件包,也越发觉得,有一天,你会陷入绝望。

对于有很多依赖关系的系统来说,发布新版本的软件包会迅速变成一场噩梦。如果依赖性规定得太紧,你会陷入 version lock (版本锁,即每次软件包的升级无法产生新的版本)。如果依赖性规定得太松,你会不可避免的面对 version promiscuity (版本泛滥,假设未来版本是需要考虑兼容性的)。当 version lock 和 version promiscuity 让你的项目无法安全而又轻松的向前推进时,这就是所谓的 dependency hell。

作为一种解决问题的办法,我提出了一套简单的规则和要求来表明版本号该如何确定和增加。这套规则基于但不仅限用于已经广泛存在的开源闭源软件的一般实践。为了让这个系统工作起来,你首先需要声明一个公有的 API,它可以由文档组成或在代码层面强制实现,且必须是清晰准确的。一旦你标识了你的公有 API,你就可以通过不同的版本号的增加来交流 API 的各种改变。设想一个形如 X.Y.Z 的版本,不影响 API 的 bug 修复会增大补丁版本,向下兼容的 API 增加或改变会增大次版本,而不兼容的 API 改变会增大主版本。

我把这套系统称作“语义化版本管理”。在这套系统之下,版本号及其改变传递了代码背后的含义,以及每个相邻版本之间的变化。


语义化版本管理规范 (SemVer) ​

原文中的关键字 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" (必须、禁止、要求、应该、不应该、推荐、可以、可选的) 在 RFC 2119 中有相应的解释和描述。

  1. 使用语义化版本管理的软件必须声明一个公有 API。该 API 可声明于代码或文档中,并且精确而全面。
  2. 一个普通的版本号必须遵循 X.Y.Z 的格式,其中 X、Y、Z 都是非负整数,禁止包含前置的 0。X 是主版本,Y 是次版本,Z 是补丁版本。每个元素必须以数字形式变大。比如:1.9.0 -> 1.10.0 -> 1.11.0。
  3. 一旦版本化的软件包被发布,那么该版本的内容就禁止被更改了。今后的任何变化都必须通过新版本的发布而产生。
  4. 主版本 0 (0.Y.Z) 表示初始开发阶段。在这个阶段任何事情都是可以更改的,公有 API 不应该被认为是稳定的。
  5. 1.0.0 版本定义了公有 API。从该版本往后,版本号的增加取决于发布的公有 API 及其变化。
  6. 只有当向下兼容的 bug 修复被引入时,补丁版本 Z (x.y.Z | x > 0) 必须被增大——bug 修复的定义是通过内部改变修复错误的特性。
  7. 如果新的向下兼容的功能被引入公有 API、如果任何公有 API 功能被废弃,次版本 Y (x.Y.z | x > 0) 必须被增大。如果显著的新功能或改进通过私有代码被引入,次版本可以被增大。次版本的被增大可以包括补丁级别的变化。当次版本被增大时,补丁版本必须被重置为 0。
  8. 如果任何不向下兼容的变化被引入时,主版本 X (X.y.z | X > 0) 必须被增大。主版本的被增大可以包括次级别和补丁级别的变化,当主版本被增大时,次版本和补丁版本必须被重置为 0。
  9. 一个预发布版本可以表示为在补丁版本之后加上一个连字符,再加上一系列由点分隔开的标识符。标识符必须仅由 ACSII 字母数字和连字符组成 [0-9A-Za-z-]。标识符禁止为空。数字标识符禁止有前置的 0。预发布版本比相应的普通版本重要性更低。一个预发布版本意味着该版本不稳定,也许没有满足相应版本既定的兼容性需求。比如:1.0.0-alpha、1.0.0-alpha.1、1.0.0-0.3.7、1.0.0-x.7.z.92。
  10. 构建元数据可以表示为在补丁版本或预发布版本之后加上一个加号,再加上一系列由点分隔开的标识符。标识符必须仅由 ASCII 字母数字和连字符组成 [0-9A-Za-z-]。标识符禁止为空。构建元数据应该在决定版本重要性的时候被忽略。也就是说,如果两个版本只有构建元数据不一样,那么它们的重要性是一样的。比如:1.0.0-alpha+01、1.0.0+20130313144700、1.0.0-beta+exp.sha.5114f85。
  11. 重要性用于版本之间在排序时的比较。重要性的计算必须分别通过主、次、补丁、预发布标识符进行 (构建元数据并不参与重要性比较)。重要性取决于下面从左到右依次比较时出现的第一个不一样的标识符:主、次、补丁版本,都是数字形式的比较。比如:1.0.0 > 2.0.0 > 2.1.0 > 2.1.1。当主、次、补丁都相同的时候,预发布版本比普通版本重要性低。比如:1.0.0-alpha < 1.0.0。两个主、次、补丁版本相同的预发布版本之间的重要性必须取决于比较每个用点分隔出的标识符,按如下形式从左到右比较出的第一个不同的标识符:纯数字的标识符用数字形式进行比较,带有字母或连字符的标识符用 ASCII 文本形式进行比较。数字标识符重要性低于非数字标识符。如果公有的字段都相同,则字段多的预发布版本重要性高于字段少的。比如: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。

为什么要使用语义化版本管理? ​

这并不是什么新的或革命性的东西。事实上你的做事习惯可能已经很接近它了。但问题是“接近”是不够的。如果不接受一些正式的规范,版本号对依赖性管理是没有实际意义的。基于上述想法,给定名词和定义,它就变得易于交流。一旦这些意图变得清晰,灵活 (且不过分的) 的依赖性规范就会最终产生。

一个简单的例子就可以向人们展示语义化版本管理会使依赖地狱成为历史。想像一个叫做“消防车”的库,它需要一个名叫“梯子”的经过语义化版本管理的软件包。当消防车被创建时,梯子的版本是 3.1.0。因为消防车使用一些在 3.1.0 被首次引入的功能,你可以安全的制定梯子的依赖关系为大于 3.1.0 且小于 4.0.0。现在当梯子的版本 3.1.1 和 3.2.0 可用时,你可以把它们发布到你的软件包管理系统之中并很清楚它们可以和现存的依赖性软件和平共处。

作为一个有责任感的开发者,你一定想要验证任何被公示的软件包的功能升级。在现实世界中这是一个混乱的地方,我们对此需要警惕但又无能为力。我们能做的就是让语义化版本管理提供给你一个清晰的路线,去发布和升级软件包,无需在依赖性软件包的不同版本中翻滚,省去你的时间和烦恼。

如果这一切是你所渴望的,你需要做的就是声明你开始遵循上述规则来进行语义化的版本管理。在你的 README 中附带这个网站的链接,让其他人了解这个规则,并从中获益。

问答时间 ​

  • 我应该如何处理 0.y.z 的初始化开发阶段呢?

最简单的事情就是当你开始初始化开发时,从 0.1.0 开始,然后在后续的发布过程中不断增加次版本。

  • 我怎么知道什么时候发布 1.0.0?

如果你的软件已经用到了产品环境,它可能应该已经是 1.0.0 了。如果你有一个稳定的用户依赖的 API,它应该是 1.0.0 了。如果你担心很多向下兼容的问题,它可能应该已经是 1.0.0 了。

  • 这和快速开发快速迭代的理念是否有冲突?

主版本 0 是快速开发时期。如果你每天都在改变 API 你应该还处在 0.y.z 或一个独立的开发分支中,这是为下一个主版本服务的。

  • 如果最小的向下不兼容的公有 API 改变都会导致新的主版本,那么我岂不是很快就做到 42.0.0 了?

这是一个开发责任感和长远意识的问题。不兼容的改变不应该很轻松就被引入到一个被大量依赖的软件当中。这必定导致升级的代价昂贵。改变主版本才可以发布不兼容的改变也在变相的促使你思考这一变化带来的影响及其投入产出比。

  • 写出整个 API 的文档是一项艰巨的工作!

作为一名开发者,撰写软件文档以供其他人使用是你的责任。管理软件复杂度是保障一个项目高效运作的及其重要的部分,如果没人知道如何使用你的软件,什么方法使用起来比较安全,项目将会变得很困难。长期来看,语义化版本管理以及一个被良好定义的公有 API 能够保障每个人每件事都运转顺利。

  • 如果我在此版本中不小心发布了一个不向下兼容的改变,我该怎么办?

当你意识到你打破了语义化版本管理规范之后,立即修复这个问题并且发布一个新的次版本去修复此问题并恢复向下兼容性。甚至在这个周期里,不要接受任何其它版本的发布。如果方便合适的话,记录下出错的版本并向你的用户告知这一问题以便他们警惕这个出错的版本。

  • 如果我更新了我自己的依赖性但是没有改变公有 API,我该怎么做?

这回被认为是兼容的,因为它并没有影响到公有 API。软件显式依赖你的软件包相同的依赖,应该有自身的以来规范,作者自然会注意任何冲突。决定这个改变是一个补丁级别还是次级别,取决于你把依赖关系的改变用在了修复一个bug上还是用在了引入新功能上。我通常会在后期期待额外的代码,很明显这是一个次级别的增大。

  • 如果我不经意改变了公有 API,而且它并没有遵循版本号的改变 (比如把补丁发布引入到了一个错误的主版本),我该怎么办?

做出做合理的判断。如果你有一大群用户,因为有意把行为改回到公有 API 会收到剧烈的影响,那么最好发布一个主版本,尽管实际的改动也许只是发布一个补丁。记住,语义化版本管理就是通过版本号的变化传递信息。如果这些改变对用户很重要,就用版本号去通知他们。

  • 我该如何处理废弃的功能?

废弃已存在的功能是软件开发的一个正常部分。而且它经常需要提前行动。当你废弃部分你的公有 API 时,你应该做两件事:(1) 更新你的文档,让用户知道这一变化,(2) 创建一个此版本发布的任务。当你发布主版本完全移除功能之前,至少要有一个次版本发布,该发布包含废弃的动作,以便让用户可以平稳的过度到新的 API。

  • 语义化版本管理是否存在版本字符串的长度限制?

没有限制,但要合理使用。比如一个 255 字符的版本字符串就算是有点长了。同样的,规范系统可以实行自己的字符串长度限制。

关于 ​

语义化版本管理规范由 Gravatars 发明者、Github 的联合创始人 Tom Preston-Werner 撰写。 如果你想留下宝贵意见,请来 Github 开一个 issue 吧

License ​

Creative Commons - CC BY 3.0