译自:语义化版本管理 2.0.0
对于一个给定的版本号 MAJOR.MINOR.PATCH (主、次、补丁),其变化的规律是:
我们还可以根据预发布、构建元数据 (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 改变会增大主版本。
我把这套系统称作“语义化版本管理”。在这套系统之下,版本号及其改变传递了代码背后的含义,以及每个相邻版本之间的变化。
原文中的关键字 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" (必须、禁止、要求、应该、不应该、推荐、可以、可选的) 在 RFC 2119 中有相应的解释和描述。
这并不是什么新的或革命性的东西。事实上你的做事习惯可能已经很接近它了。但问题是“接近”是不够的。如果不接受一些正式的规范,版本号对依赖性管理是没有实际意义的。基于上述想法,给定名词和定义,它就变得易于交流。一旦这些意图变得清晰,灵活 (且不过分的) 的依赖性规范就会最终产生。
一个简单的例子就可以向人们展示语义化版本管理会使依赖地狱成为历史。想像一个叫做“消防车”的库,它需要一个名叫“梯子”的经过语义化版本管理的软件包。当消防车被创建时,梯子的版本是 3.1.0。因为消防车使用一些在 3.1.0 被首次引入的功能,你可以安全的制定梯子的依赖关系为大于 3.1.0 且小于 4.0.0。现在当梯子的版本 3.1.1 和 3.2.0 可用时,你可以把它们发布到你的软件包管理系统之中并很清楚它们可以和现存的依赖性软件和平共处。
作为一个有责任感的开发者,你一定想要验证任何被公示的软件包的功能升级。在现实世界中这是一个混乱的地方,我们对此需要警惕但又无能为力。我们能做的就是让语义化版本管理提供给你一个清晰的路线,去发布和升级软件包,无需在依赖性软件包的不同版本中翻滚,省去你的时间和烦恼。
如果这一切是你所渴望的,你需要做的就是声明你开始遵循上述规则来进行语义化的版本管理。在你的 README 中附带这个网站的链接,让其他人了解这个规则,并从中获益。
最简单的事情就是当你开始初始化开发时,从 0.1.0 开始,然后在后续的发布过程中不断增加次版本。
如果你的软件已经用到了产品环境,它可能应该已经是 1.0.0 了。如果你有一个稳定的用户依赖的 API,它应该是 1.0.0 了。如果你担心很多向下兼容的问题,它可能应该已经是 1.0.0 了。
主版本 0 是快速开发时期。如果你每天都在改变 API 你应该还处在 0.y.z 或一个独立的开发分支中,这是为下一个主版本服务的。
这是一个开发责任感和长远意识的问题。不兼容的改变不应该很轻松就被引入到一个被大量依赖的软件当中。这必定导致升级的代价昂贵。改变主版本才可以发布不兼容的改变也在变相的促使你思考这一变化带来的影响及其投入产出比。
作为一名开发者,撰写软件文档以供其他人使用是你的责任。管理软件复杂度是保障一个项目高效运作的及其重要的部分,如果没人知道如何使用你的软件,什么方法使用起来比较安全,项目将会变得很困难。长期来看,语义化版本管理以及一个被良好定义的公有 API 能够保障每个人每件事都运转顺利。
当你意识到你打破了语义化版本管理规范之后,立即修复这个问题并且发布一个新的次版本去修复此问题并恢复向下兼容性。甚至在这个周期里,不要接受任何其它版本的发布。如果方便合适的话,记录下出错的版本并向你的用户告知这一问题以便他们警惕这个出错的版本。
这回被认为是兼容的,因为它并没有影响到公有 API。软件显式依赖你的软件包相同的依赖,应该有自身的以来规范,作者自然会注意任何冲突。决定这个改变是一个补丁级别还是次级别,取决于你把依赖关系的改变用在了修复一个bug上还是用在了引入新功能上。我通常会在后期期待额外的代码,很明显这是一个次级别的增大。
做出做合理的判断。如果你有一大群用户,因为有意把行为改回到公有 API 会收到剧烈的影响,那么最好发布一个主版本,尽管实际的改动也许只是发布一个补丁。记住,语义化版本管理就是通过版本号的变化传递信息。如果这些改变对用户很重要,就用版本号去通知他们。
废弃已存在的功能是软件开发的一个正常部分。而且它经常需要提前行动。当你废弃部分你的公有 API 时,你应该做两件事:(1) 更新你的文档,让用户知道这一变化,(2) 创建一个此版本发布的任务。当你发布主版本完全移除功能之前,至少要有一个次版本发布,该发布包含废弃的动作,以便让用户可以平稳的过度到新的 API。
没有限制,但要合理使用。比如一个 255 字符的版本字符串就算是有点长了。同样的,规范系统可以实行自己的字符串长度限制。
语义化版本管理规范由 Gravatars 发明者、Github 的联合创始人 Tom Preston-Werner 撰写。 如果你想留下宝贵意见,请来 Github 开一个 issue 吧。
译自:语义化版本管理 2.0.0
对于一个给定的版本号 MAJOR.MINOR.PATCH (主、次、补丁),其变化的规律是:
我们还可以根据预发布、构建元数据 (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 改变会增大主版本。
我把这套系统称作“语义化版本管理”。在这套系统之下,版本号及其改变传递了代码背后的含义,以及每个相邻版本之间的变化。
原文中的关键字 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" (必须、禁止、要求、应该、不应该、推荐、可以、可选的) 在 RFC 2119 中有相应的解释和描述。
这并不是什么新的或革命性的东西。事实上你的做事习惯可能已经很接近它了。但问题是“接近”是不够的。如果不接受一些正式的规范,版本号对依赖性管理是没有实际意义的。基于上述想法,给定名词和定义,它就变得易于交流。一旦这些意图变得清晰,灵活 (且不过分的) 的依赖性规范就会最终产生。
一个简单的例子就可以向人们展示语义化版本管理会使依赖地狱成为历史。想像一个叫做“消防车”的库,它需要一个名叫“梯子”的经过语义化版本管理的软件包。当消防车被创建时,梯子的版本是 3.1.0。因为消防车使用一些在 3.1.0 被首次引入的功能,你可以安全的制定梯子的依赖关系为大于 3.1.0 且小于 4.0.0。现在当梯子的版本 3.1.1 和 3.2.0 可用时,你可以把它们发布到你的软件包管理系统之中并很清楚它们可以和现存的依赖性软件和平共处。
作为一个有责任感的开发者,你一定想要验证任何被公示的软件包的功能升级。在现实世界中这是一个混乱的地方,我们对此需要警惕但又无能为力。我们能做的就是让语义化版本管理提供给你一个清晰的路线,去发布和升级软件包,无需在依赖性软件包的不同版本中翻滚,省去你的时间和烦恼。
如果这一切是你所渴望的,你需要做的就是声明你开始遵循上述规则来进行语义化的版本管理。在你的 README 中附带这个网站的链接,让其他人了解这个规则,并从中获益。
最简单的事情就是当你开始初始化开发时,从 0.1.0 开始,然后在后续的发布过程中不断增加次版本。
如果你的软件已经用到了产品环境,它可能应该已经是 1.0.0 了。如果你有一个稳定的用户依赖的 API,它应该是 1.0.0 了。如果你担心很多向下兼容的问题,它可能应该已经是 1.0.0 了。
主版本 0 是快速开发时期。如果你每天都在改变 API 你应该还处在 0.y.z 或一个独立的开发分支中,这是为下一个主版本服务的。
这是一个开发责任感和长远意识的问题。不兼容的改变不应该很轻松就被引入到一个被大量依赖的软件当中。这必定导致升级的代价昂贵。改变主版本才可以发布不兼容的改变也在变相的促使你思考这一变化带来的影响及其投入产出比。
作为一名开发者,撰写软件文档以供其他人使用是你的责任。管理软件复杂度是保障一个项目高效运作的及其重要的部分,如果没人知道如何使用你的软件,什么方法使用起来比较安全,项目将会变得很困难。长期来看,语义化版本管理以及一个被良好定义的公有 API 能够保障每个人每件事都运转顺利。
当你意识到你打破了语义化版本管理规范之后,立即修复这个问题并且发布一个新的次版本去修复此问题并恢复向下兼容性。甚至在这个周期里,不要接受任何其它版本的发布。如果方便合适的话,记录下出错的版本并向你的用户告知这一问题以便他们警惕这个出错的版本。
这回被认为是兼容的,因为它并没有影响到公有 API。软件显式依赖你的软件包相同的依赖,应该有自身的以来规范,作者自然会注意任何冲突。决定这个改变是一个补丁级别还是次级别,取决于你把依赖关系的改变用在了修复一个bug上还是用在了引入新功能上。我通常会在后期期待额外的代码,很明显这是一个次级别的增大。
做出做合理的判断。如果你有一大群用户,因为有意把行为改回到公有 API 会收到剧烈的影响,那么最好发布一个主版本,尽管实际的改动也许只是发布一个补丁。记住,语义化版本管理就是通过版本号的变化传递信息。如果这些改变对用户很重要,就用版本号去通知他们。
废弃已存在的功能是软件开发的一个正常部分。而且它经常需要提前行动。当你废弃部分你的公有 API 时,你应该做两件事:(1) 更新你的文档,让用户知道这一变化,(2) 创建一个此版本发布的任务。当你发布主版本完全移除功能之前,至少要有一个次版本发布,该发布包含废弃的动作,以便让用户可以平稳的过度到新的 API。
没有限制,但要合理使用。比如一个 255 字符的版本字符串就算是有点长了。同样的,规范系统可以实行自己的字符串长度限制。
语义化版本管理规范由 Gravatars 发明者、Github 的联合创始人 Tom Preston-Werner 撰写。 如果你想留下宝贵意见,请来 Github 开一个 issue 吧。