简述
SemVer 是 Semantic Versioning (语义化版本控制)的简称,中文通常叫做语义化版本号
其核心思想是:
版本号不仅仅是一个编号,还要表达与上一个版本之间的兼容关系
形象点说就是当你看到一个版本号从 1.1.0变成了 1.2.0,你就大概知道这是添加了新功能;看到从 1.2.0变成了 2.0.0,你就大概知道这是进行了一些破坏性更新,可能不会兼容之前的版本
基本
格式
基本版本格式
最基本格式:
X.Y.Z
对应
X -> MAJOR(主版本号)
Y -> MINOR(次版本号)
Z -> PATCH(修订号)
要求:
- 必须由三个数字组成
- 三个数字之间用英文句点
.分隔 - 数字表示非负整数
- 不能有前导零(例如
1.02.3、01.0.0是不合法的) - 每个元素必须数值递增(例如
1.9.0 -> 1.10.0 -> 1.11.0)
例如:
1.0.0
2.3.4
10.20.30
注意:
0.y.z版本用于初始开发阶段,任何内容都可能随时变化,公共 API 不应被视为稳定。1.0.0才正式定义了公共 API
先行版本格式
先行版本,也叫预发布版本(prerelease)
格式是在 X.Y.Z 后面加 - :
X.Y.Z-prerelease
例如:
1.0.0-alpha
1.0.0-alpha.1
1.0.0-beta
1.0.0-rc.1
要求:
- 用英文句点
.分隔 - 每个标识符只能包含:
- ASCII 字母:
A-Z、a-z - 数字:
0-9 - 连接号:
-
- ASCII 字母:
- 不能有空白字符
- 数字型标识符不能有前导零
优先级:
1.0.0-alpha < 1.0.0
先行版本的优先级低于对应的正式版本
当两个先行版本具有相同的主版本号、次版本号和修订号时,优先级按照以下规则从左到右逐个标识符比较:
- 纯数字标识符按数值大小比较(例如
2 < 11) - 包含字母或连字符的标识符按 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.1 < 1.0.0
实际项目中你不一定需要记这么细,但要知道
alpha < beta < rc < 正式版
常见标识:
| 标识 | 含义 | 形象解释 |
|---|---|---|
alpha |
内测版 | 早期、不稳定 |
beta |
公测版 | 功能基本完成但可能有 bug |
rc |
Release Candidate,候选发布版 | 功能完成且没有明显 bug |
dev |
开发版 | |
snapshot |
快照版 |
一个版本可能会这样迭代:
1.0.0-alpha.1
1.0.0-alpha.2
1.0.0-beta.1
1.0.0-beta.2
1.0.0-rc.1
1.0.0-rc.2
1.0.0
构建信息格式
构建信息( build metadata),格式是在 X.Y.Z 后面加 + :
X.Y.Z+build
或者在 prerelease 后面加:
X.Y.Z-prerelease+build
例如:
1.0.0+20260101045141
1.0.0+build.123
1.0.0+git.abc123
1.0.0-alpha+001
1.0.0-beta+exp.sha.5114f85
要求:
- 用英文句点
.分隔 - 每个标识符只能包含:
- ASCII 字母:
A-Z、a-z - 数字:
0-9 - 连接号:
-
- ASCII 字母:
- 不能有空白字符
优先级:
构建信息不影响版本优先级,也就是说
1.0.0+build.1
和
1.0.0+build.2
在版本优先级上相同
构建元信息用于记录构建相关的信息。例如 1.1.4+build.514 表示这是 1.1.4 版本的第 514 次构建,1.1.4+20260514 表示该版本于 20260514 构建,1.1.4+git.abc5141 表示该版本由 Git commit abc5141 构建而来
当然这些都不是强制语义,只是约定。SemVer 标准只规定了
+的后面是 build metadata,具体的命名规则由你的团队、构建系统自行定义。常见的有git.xxx、sha.xxx、commit.xxx(通常表示 Git commit hash)、build.xxx、ci.xxx等
完整格式
完整格式为:
X.Y.Z-prerelease+build
例如:
1.2.3-alpha.1+build.456
拆开就是:
| 版本号 | 解释 |
|---|---|
| 1.2.3 | 标准版本号 |
| -alpha.1 | 先行版本号 |
| +build.456 | 构建信息 |
迭代规则
PATCH
PATCH 表示修订号,当你做了向后兼容的问题修正时,递增 PATCH(前提是主版本号 x > 0):
1.2.3 -> 1.2.4
常见情况:
- 修复了若干 bug
- 修复安全漏洞
- 修复兼容性问题
- 重构了代码但既没有添加新功能也没有进行破坏性变更
- ......
MINOR
MINOR 表示次版本号,当你做了向后兼容的新功能或将公共 API 中的功能标记为**弃用(deprecated)**时,递增 MINOR(前提是主版本号 x > 0):
1.2.3 -> 1.3.0
MINOR 递增时,PATCH 需归零
比如
1.2.3要进行次版本号迭代,那么应该是:1.2.3 -> 1.3.0 (PATCH归零)而不是
1.2.3 -> 1.3.3 (PATCH没有归零,这是错的)
常见情况:
- 新增配置项
- 新增 API
- 新增功能
- 将公共 API 中的功能标记为弃用(deprecated)
- 优化性能但不改变程序行为
- ......
MAJOR
MAJOR 表示主版本号,当你对公共 API 做了不兼容的修改时,递增 MAJOR:
1.2.3 -> 2.0.0
MAJOR 递增时,PATCH 和 MINOR 需归零
比如
1.2.3进行了不兼容的修改:1.2.3 -> 2.0.0 (MINOR和PATCH都归零)而不是
1.2.3 -> 2.2.3 (MINOR和PATCH没有归零,这是错误的)
常见情况:
- 协议不兼容
- 配置文件格式变动
- 用户升级后必须修改配置
- 数据格式变动
- ......
此外,已发布的版本内容不可修改,任何修改都必须作为新版本发布
兼容
SemVer 最重要的不是数字本身,而是兼容性承诺
向后兼容
向后兼容,也叫 backward compatible。意思是用户原来基于旧版本写的代码,在最新版本依然可以正常工作
例如旧版本支持:
sum(1, 2)
在新版本仍然支持这样写。与此同时增加了新能力:
sum([1, 2, 3])
旧代码也可以按照旧版本的方式使用,这就是向后兼容。升级 MINOR
不兼容变更
不兼容变更,也叫 breaking change 或者破坏性变更。意思是用户升级后,原来的代码、配置、数据、命令可能不能直接用了
例如旧版本是这样的:
login(username, password)
而新版本改成了这样:
signIn(username, password)
这就是不兼容变更,应该迭代 MAJOR
除此之外,这些也叫不兼容变更
-
改变默认行为
假如旧版本默认
自动重试 3 次,而新版本默认不自动重试。即使 API 没变,但行为变了也算是不兼容变更 -
修改返回值结构
旧版本:
{ "id": 1, "name": "huajibenji" }新版本:
{ "userId": 1, "displayName": "huajibenji" }旧代码可能依然会读取
id和name,所以这是不兼容变更 -
修改参数顺序
旧版本:
createUser(name, age)新版本:
createUser(age, name)旧代码可能依然会按照
name, age的顺序进行读取,所以这也是不兼容变更
实践
依赖管理中的 SemVer
SemVer 在管理器里非常重要,常见的有:
- npm
- pnpm
- Cargo
- yarn
- ......
以 npm 为例,我们经常可以在依赖中看到:
{
"dependencies": {
"lodash": "^4.17.21",
"axios": "~1.6.0"
}
}
你可能会问,这里的 ^ 和 ~是什么意思?别急,下面会讲
精准匹配版本
比如这样:
"axios": "1.6.0"
表示只安装 1.6.0 这个版本
~ 波浪号范围
"axios": "~1.6.0"
表示允许 PATCH 升级:
>=1.6.0 <1.7.0
也就是可以安装:
1.6.1
1.6.2
1.6.9
但是不会安装
1.7.0
2.0.0
^ 脱字符范围
这样:
"axios": "^1.6.0"
表示不允许破坏性更新。对于 1.6.0 ,等价于
>=1.6.0 <2.0.0
也就是可以安装
1.6.1
1.7.0
1.8.5
但是不会安装
2.0.0
因为按照 SemVer,1.x.x 范围内的 MINOR 和 PATCH 都应该兼容
但
^在0.x版本中的表现可能会不太一样因为
0.x表示不稳定,所以^在0.x中更保守。例如"some-lib": "^0.2.3"通常等价于:
>=0.2.3 <0.3.0也就是说不会自动升级到大于等于
0.3.0的版本,因为在此以上的版本都可能会有不兼容的变更
SemVer 和 Changelog
SemVer 最好配合 CHANGE LOG 使用,比如:
## 1.3.0 - 2026-07-09
### Added
- 新增用户导出功能
- 新增 CSV 格式支持
### Fixed
- 修复分页参数为 0 时崩溃的问题
### Deprecated
- 标记 `getUserInfo` 为废弃,建议使用 `getUser`
## 1.2.1 - 2026-06-20
### Fixed
- 修复登录 token 刷新失败的问题
常见分类:
| 分类 | 含义 |
|---|---|
| Added | 新增功能 |
| Changed | 修改已有行为 |
| Deprecated | 标记废弃 |
| Removed | 删除功能 |
| Fixed | 修复问题 |
| Security | 安全修复 |
如果有不兼容变更,应该明确写:
### Breaking Changes
- 删除 `login(username, password)`,请改用 `signIn({ username, password })`
Deprecated:废弃但不删除
在 SemVer 中,如果你想移除一个 API,最好不要马上就删。一般推荐的流程是:
1.2.0 标记旧 API 为 deprecated,但仍然可用
1.3.0 继续提醒用户迁移
2.0.0 删除旧 API
这样可以给足用户迁移的时间
0.x 版本的特殊含义
这通常表示项目处于初始开发阶段,比如:
0.1.0
0.1.4
0.5.14
0.9.0
在 0.x 阶段,项目通常还没承诺稳定兼容。例如:
0.3.0 → 0.4.0
这看起来好像只是一个 MINOR 变化,但是这也可能包含不兼容变更
一般情况下,一个项目迭代到 1.0.0 版本才表示这个项目已经有稳定的公共 API,可以用于生产或正式使用
一般情况下,项目一般都会从
0.1.0开始迭代
常见问题
Q & A
一般什么时候发 1.0.0 ?
通常满足这些条件就可以发 1.0.0:
- 核心功能已经完整
- API 基本稳定
- 文档比较完整
- 测试覆盖较好
- 已经有人或生产环境使用
- 后续会认真遵守兼容性规则
- 不希望随意破坏用户使用方式
v1.x.x算是 SemVer 吗?
不算。但是,在语义化版本号之前增加前缀 v 是用来表示版本号的常用做法
在版本控制系统中,将 version 缩写为 v 是很常见的,比如:
git tag v1.2.3 -m "Release version 1.2.3"
中 v1.2.3 表示标签名称,而 1.2.3 是语义化版本号
引用自semver.org
prerelease 是怎么迭代的?
比如 1.0.0-alpha.1 -> 1.0.0-alpha.2 算版本迭代吗?
算,虽然 1.0.0 没变,但完整版本号已经变了:
1.0.0-alpha.1
1.0.0-alpha.2
所以它们是两个不同的版本。根据 SemVer 规则:
1.0.0-alpha.1 < 1.0.0-alpha.2 < 1.0.0
你可能会问:代码变了,为什么不一定要改 X.Y.Z?
因为 prerelease 部分本身就是版本号的一部分。
1.0.0-alpha.1
可以理解为:
1.0.0正式版发布前的第一个 alpha 版本
而
1.0.0-alpha.2
可以理解为:
1.0.0正式版发布前的第二个 alpha 版本。
所以只要完整版本号变化了,就已经是新版本,不一定必须改 X.Y.Z
一句话总结就是:1.0.0-alpha.1 和 1.0.0-alpha.2 是两个不同的版本,虽然它们的目标正式版都是 1.0.0,但它们是两个不同的 prerelease 版本
[!IMPORTANT]
个人理解,此处仅供参考。因为官方没有定义过 prerelease 的迭代
1.0.0-alpha -> 1.0.1-alpha 合法吗?
合法,但语义变了:
1.0.0-alpha
表示 1.0.0 的预发布版本
1.0.1-alpha
表示 1.0.1 的预发布版本
所以这不是同一个正式版本的 prerelease 迭代,而是目标正式版本从 1.0.0 变成了 1.0.1
alpha、beta、rc 是 SemVer 强制要求的吗?
不是。SemVer 只规定 prerelease 的格式和排序规则,不强制使用 alpha、beta、rc
这块内容 更多取决于项目或者个人的规定
常见误区
版本号是小数
很多人会误以为
1.10.0 < 1.9.0
因为按小数看 1.10 等于 1.1
实则不然,版本号不是小数,而是多个整数段
这点在 SemVer 官方文档中也讲到过