Skip to content

语义化版本

Semantic Versioning 2.0.0

语义化版本(Semantic Versioning,简称 SemVer) 是一种软件版本号的命名规范,由 Tom Preston-Werner 于 2011 年提出,现在已被广泛应用于开源社区(如 npm、Cargo、Composer、PyPI 等)和现代软件开发流程中。

其核心思想是:通过版本号本身传达变更的含义,让开发者清楚地知道升级某个版本可能会带来什么影响。

版本格式

标准的语义化版本格式为:

MAJOR.MINOR.PATCH[-预发布标识][+构建元数据]

例如:

  • 2.3.1

  • 1.0.0-alpha.1

  • 3.1.4-beta.2+build.20250512

各部分的含义

部分 含义 何时递增 对兼容性的影响
MAJOR (主版本) 引入了不兼容的 API 变更 当有破坏性改动(Breaking Change)时 不兼容
MINOR (次版本) 增加了向下兼容的新功能 新增特性,但不破坏现有功能 兼容
PATCH (修订版) 进行了向下兼容的 bug 修复 只修复 bug,不改变 API 兼容

预发布标识(Pre-release):如 -alpha.1-beta.2-rc.3,表示该版本不稳定,可能存在 bug,不建议在生产环境使用。

构建元数据(Build metadata):如 +build.123+sha.abc123,通常用于 CI/CD 构建信息,不影响版本比较。

版本比较规则

  • 1.0.02.0.0 不兼容

  • 1.2.31.3.0 兼容(新增功能)

  • 1.2.31.2.4 兼容(bug 修复)

  • 预发布版本总是低于对应的正式版本(例如 1.0.0-alpha < 1.0.0

常见约定

  • 0.y.z 版本(主版本为 0):表示初始开发阶段,API 可能随时发生不兼容变更。

  • 1.0.0 通常标志着第一个稳定版本

  • 公开 API:只有对外公开的接口才需要遵循语义化版本,内部实现变更不一定要递增 MAJOR。

  • ^ 和 ~ 的含义(常见于 package.json):

  • ^1.2.3 = >=1.2.3 <2.0.0(允许 MINOR 和 PATCH 升级)

  • ~1.2.3 = >=1.2.3 <1.3.0(只允许 PATCH 升级)

优点

  • 清晰传达变更影响

  • 便于自动化依赖管理(例如 Dependabot、npm 等)

  • 降低升级风险

  • 促进团队和生态系统形成统一的版本沟通语言

实际例子

  • React 从 0.x 升级到 16.0.017.0.018.0.0 时都进行了重大变更,因此主版本递增。

  • Linux 内核使用的是自己的版本方案,但很多周边工具(如 Docker、Kubernetes)都严格遵循 SemVer。

更新项目元数据、文档、构建流水线等是否需要迭代修订号?

不是严格只修改源码才迭代修订号(PATCH),但核心原则是围绕“向下兼容的 bug 修复”。

  • PATCH 版本(修订号)MUST只引入向下兼容的 bug 修复时递增。

  • Bug fix 的定义:一个内部变更,用于修正不正确的行为(internal change that fixes incorrect behavior)。

这意味着:

  • 主要针对代码中的错误修复(修复 bug、崩溃、安全漏洞、性能问题等),且不改变公共 API 的行为

  • 只要是修复“错误行为”,即使涉及源码修改,也用 PATCH。

因此一般不需要专门为这些更新递增 PATCH,但实际处理有以下常见做法:

  1. 纯文档更新(README、注释、示例代码、文档网站等)

    • 如果只是修正错别字、格式、澄清说明,通常不需要版本迭代,或者只作为补丁发布(很多人会用 PATCH)。

    • 如果文档是公共 API 声明的一部分(例如 API 文档与实际行为不符),修正它本质上是“修复不正确行为”,可以视为 bug fix,用 PATCH。

  2. 构建流水线、CI/CD 配置、项目元数据(package.json 中的描述、作者信息等)

    • 这些通常不影响公共 API,也不属于“修复不正确的运行时行为”。

    • 常见处理方式:

      • 不递增版本,直接在当前版本上更新(尤其是内部工具或私有项目)。

      • 如果必须发布新包/镜像,则递增 PATCH(视为维护性改进)。

      • 有些项目会用构建元数据(+build.xxx)来区分,而不改变核心版本号。

  3. 核心原则:版本号是为使用者服务的。问自己:“这个变更是否会让依赖我包的用户感到意外?”

    • 纯文档/流水线变更 → 用户通常感受不到 → 可以不 bump 或用 PATCH。

    • 修复了实际 bug(即使是文档描述的错误行为)→ 用 PATCH。

  4. 推荐做法

    • 维护 CHANGELOG.md,清楚标注每一项变更属于哪类。

    • 使用自动化工具(如 semantic-release + Conventional Commits),通过 commit 消息自动决定版本类型(fix: → PATCH)。

    • 小型变更可以积累几个后再统一发布一个 PATCH 版本,避免版本号碎片化。

修订号(PATCH)主要为代码 bug 修复准备,但文档和构建变更通常用 PATCH 发布或不单独发布,具体取决于是否需要让下游用户拉取新版本。规范更关注“公共 API 的实际行为是否正确”,而不是严格区分“改没改源码”。