工程实践

CHANGELOG.md:里面该写什么,怎样保持更新

阅读约 1 分钟

CHANGELOG.md 是放在仓库根目录的一个 Markdown 文件,按已发布的版本逐一列出改了什么,并且用项目用户能据此行动的话来写。读它的人包括在决定要不要升级的人、升级后在排查问题的人,以及把它渲染到项目页面上的软件包仓库和发布工具。大多数项目遵循的格式,就是 keepachangelog.com 描述的那一种:最新版本在最上面,上面再放一个 Unreleased 分节,条目归入 Added、Changed、Deprecated、Removed、Fixed 和 Security 之下。

分节放什么示例行
Unreleased已合并但尚未进入某个版本的改动- Export now streams, so files over 100 MB no longer time out.
Added用户能用到的新能力- \—dry-run` flag on `deploy` prints the plan without applying it.`
Changed原有行为现在的工作方式变了- Default timeout raised from 30 s to 60 s.
Deprecated仍然可用,但已计划移除- \legacyAuth()` will be removed in 3.0; use `auth()`.`
Removed在这个版本里已经没有了- Dropped Node 16 support.
Fixed用户可能遇到过的缺陷- Search no longer fails on queries containing a hyphen.
Security读者今天就应该为之升级的修复- Fixed a path traversal in the static file handler (CVE pending).

CHANGELOG.md 文件是什么?

它是一个项目供人阅读的历史,一次一个版本,放在代码旁边,随代码一起发布。它和 git log 的区别在于受众和取舍:git log 为写提交的人记录每一次提交,而体验日志记录的是用户会注意到的变化,写给没有写这些代码的人看。一次重命名内部变量的提交,在体验日志里没有对应的行。一次改动默认超时的提交则有,哪怕 diff 只改了一个字符。

这个文件放在仓库根目录,并且用这个确切的名字,是因为工具会到那里去找它。GitHub 会从仓库页面链接到它,npm 和 PyPI 在它被打进包里时会渲染它,而 release-please 和 semantic-release 这类发布工具会就地读写它。小写的 changelog.md 和 CHANGES.md 对人来说同样可用,但有些工具只认那个规范的名字。

一份完整的 CHANGELOG.md 长什么样?

一份简短而正确的示例,比一大段描述更有价值。下面这份遵循 Keep a Changelog 1.1.0 的排版,版本号则遵循语义化版本。

# Changelog

All notable changes to this project are listed here.
Format: Keep a Changelog. Versions: Semantic Versioning.

## [Unreleased]

### Added
- `--dry-run` flag on `deploy` prints the plan without applying it.

## [2.3.1] - 2026-10-01

### Fixed
- Search no longer fails on queries containing a hyphen. Broken
  since 2.3.0.

### Security
- Fixed a path traversal in the static file handler. Upgrade today
  if you serve user-uploaded files.

## [2.3.0] - 2026-09-18

### Added
- Streaming export. Files over 100 MB no longer time out.

### Deprecated
- `legacyAuth()` still works but will be removed in 3.0.
  Use `auth()`.

[Unreleased]: https://github.com/acme/tool/compare/v2.3.1...HEAD
[2.3.1]: https://github.com/acme/tool/compare/v2.3.0...v2.3.1
[2.3.0]: https://github.com/acme/tool/compare/v2.2.0...v2.3.0

四个细节承担了大部分价值。每个版本都有一个 ISO 格式的日期,读者可以把它和自己的问题开始的时间对上。每个版本标题都是指向 compare 视图的链接,完整的 diff 只需一次点击。每条修复都说明是哪个版本引入的问题。而 Security 分节单独放着,那些在扫视”我现在需要升级吗”的人,几秒钟就能找到它。

如何让 CHANGELOG.md 保持更新?

在改动所在的同一个 pull request 里写下那一行,放在 Unreleased 下,发布时再把这一节挪到一个版本标题下。方法就是这么多。文件之所以会过时,是因为那一行是后来由别人、在发布时、根据一堆不是自己写的提交信息补上的。

有两件事能让这个习惯坚持下去。第一是 CI 里的一项检查:一个改动了 src/ 却没碰体验日志的 pull request 直接失败,并提供一个标签给那些没有用户可见影响的改动跳过检查;用 GitHub Action 做体验日志检查展示了那个四十行的版本。第二是一个发布步骤,用一条命令把 Unreleased 改名为版本号和日期,这样没人需要记着手动去做。谁来写体验日志讲了团队如何解决”这一行是谁的活”这个问题。

体验日志应该从提交生成吗?

生成能给你一份完整的草稿,但给不了你一份体验日志。基于 conventional commits 的工具,以及 release-please 这样的发布自动化,会读取 feat:、fix: 和 BREAKING CHANGE: 前缀,产出一份分好组、每个 pull request 都带链接的列表。这份列表的质量取决于提交主题,而提交主题描述的是代码。

可行的折中是先生成,再编辑。让工具根据提交创建 Unreleased 分节,然后在发布的 pull request 里重写用户会读到的那些行:用症状代替函数名,用挪了位置的设置代替改动的文件。从 Conventional Commits 到体验日志完整走了一遍这条流水线,而一个在浏览器里运行的体验日志生成器,不用安装任何东西,就足以从粘贴进来的 git log 得到一份草稿。

哪些内容不该写进 CHANGELOG.md?

内部重构、没有行为变化的依赖升级、只涉及测试的提交、CI 配置和格式化改动。它们属于 git 历史,需要的人可以在那里找到。把它们写进来,会埋没真正重要的行,并教会读者跳过这个文件。

另一样要留在外面的,是一项改动的营销版本。“极速导出”没有给读者任何可以核实的东西。“导出现在是流式的;超过 100 MB 的文件不再超时”则有。这个文件是参考文档,语气也应该与之相称:短句、具体的名词、每一项都带版本或日期。Keep a Changelog,实践版讲了规范里团队最常丢掉的部分,体验日志文件格式则讲了什么时候在 Markdown 文件旁边放一份 JSON 或 YAML 源文件才值得。

有了体验日志页面的产品还需要这个文件吗?

库需要,因为软件包仓库渲染的就是这个文件,开发者离线读的也是它。一个没有可安装产物的 SaaS 产品,只靠页面也过得去,因为它的用户从不会看到仓库。很多团队两者都维护:文件作为工程记录,再加一个从它写出来、面向更广受众的公开体验日志页面,细节的划分方式就像体验日志与发布说明描述的那样。如果页面的内容来自文件,就让文件保持唯一事实来源,页面由它生成,这样只有一个地方需要编辑。

FAQ

CHANGELOG.md 和发布说明有什么区别? 体验日志是所有值得注意的变化的完整连续记录,保存在仓库里。发布说明是关于某一次发布的一条消息,通常更短,并且为更广的受众改写过。很多项目直接把某个版本在体验日志里的那一节作为它的发布说明发布。

CHANGELOG.md 应该放在哪里? 放在仓库根目录,用这个确切的名字,这样 GitHub、软件包仓库和发布工具才能找到它。Monorepo 每个包各放一份,和各自的清单文件放在一起。

我应该从 git 提交生成 CHANGELOG.md 吗? 先生成草稿,再编辑用户会读到的那些行。提交信息描述的是代码改动;体验日志描述的是用户注意到的变化。conventional-changelog 和 release-please 这类工具能给出一个不错的起点。

每一次提交都需要一条体验日志条目吗? 不需要。只有项目用户可能注意到的变化才需要:新能力、行为改变、弃用、移除、修复和安全补丁。重构、测试改动和 CI 修改留在 git 历史里就好。


本文的技术内容未经独立审核。如有错误,请告诉我们,我们会更正。

changeloop 相关页面: changelog 生成器, changelog 示例

changeloop
打造闭环 changelog 的团队。用户提出需求,你的团队交付,提出需求的人得知结果。