Keep a Changelog,真正落地实践之后的心得
阅读约 1 分钟 更新于
Keep a Changelog 是一份关于 CHANGELOG.md 的一页纸惯例:最新版本排在最前,每个版本一个小节,带有版本号和 ISO 日期,条目按六种类型分组(Added、Changed、Deprecated、Removed、Fixed、Security),顶部还有一个 Unreleased 小节用来放发布之间的条目。大多数引用它的团队只落实了大约三分之二,而他们放弃的那三分之一,恰恰是保护用户的那三分之一。
Olivier Lacan 在 2014 年发布了 Keep a Changelog,其中一句话比大多数软件领域的文字都更经得起时间考验:别让你的朋友把 git log 直接倒进体验日志里。十年过去,它已经是这个软件角落里最接近标准的东西了。直接读原文比读摘要更值得,这篇文章谈的正是那些容易被丢掉的部分。
Keep a Changelog 要求了什么
在仓库根目录放一份 CHANGELOG.md,最新的排在最前,每个版本一个小节。每个版本都带有版本号和 ISO 日期,并把条目分到六种类型下:
| 类型 | 用途 | 丢掉它的代价 |
|---|---|---|
| Added | 新功能 | 没有代价;没人会丢掉这个 |
| Changed | 现有行为的变化 | 读者只能从报错中发现行为已经变了 |
| Deprecated | 即将被移除的功能 | 一次移除会变成事故,而不是一次有计划的事件 |
| Removed | 本次发布中被移除的功能 | 没人能分清这是一次移除还是一个 bug |
| Fixed | 错误修复 | 没有代价;这个也没人会丢掉 |
| Security | 安全漏洞 | 唯一正在搜索它的那位读者会找不到它 |
再加上顶部的一个 Unreleased 小节,这样条目一旦合并就有地方可以放,任何人也都能看到即将到来的内容。
这几乎就是全部内容了。剩下的都是理由:条目是写给人看的,每个变化一条条目,这份文件是一份文档,而不是一份日志。
Keep a Changelog 的哪些部分容易被丢掉
依次是 Unreleased 小节、接着是六种类型中的四种,Security 也在其中。
Unreleased 最先消失。 它是没有截止日期压着的那个小节,所以维护它的动作也最先停下来,一旦它消失,条目就会在发布时从提交历史里被临时写出来。这正是规范一开篇就在警告的 git log 大杂烩,只不过是逐步走到这一步的。体验日志自动化一文谈的大体上就是,如何在没人特意记得的情况下让这个小节保持活跃。
六种类型会坍缩成两种。 大多数真实的体验日志最后都只剩下 Added 和 Fixed,因为 Changed 和 Deprecated 需要对”有人依赖了什么”做出判断,而这种判断正是有价值的部分。尤其是 Deprecated,它是唯一一种关于未来的承诺,丢掉它正是一次移除演变成事故的原因;维持这份承诺的具体做法在如何停用一个 API一文中。
Security 不再被单独列出。 一条归在 Fixed 下的安全修复,对唯一正在搜索它的那位读者来说是不可见的。即使修复很小,也要把它单独列出来,尤其是在你并不想引人注意的时候。
这份规范没有回答什么
它是一种文件格式。对于你采用它之后立刻会遇到的那些问题,它什么都没说:
- 别人怎么知道? 仓库里的一份文件能触达贡献者,触达不了从未打开过 GitHub 的客户。
- 没有版本号的产品怎么办? 一个持续部署的服务没有 v4.2.0 可以用来分组。大多数团队会改用日期,这样也行得通,规范既不认可也不禁止这种做法。
- 谁来写条目? 规范假设是人来写,但没说是什么时候写。
- 多个不同的读者群体怎么办? 一份文件服务的是开发者。它没法把同样的内容提供给一个非技术背景的管理员,为他们手工重新排版正是重复工作开始的地方。体验日志与发布说明正是这份规范留给你自己去做的那次拆分。
这个想法更严格的一个分支,Common Changelog,收紧了其中一些地方:它禁止某些条目的措辞方式,要求链接到具体的变化,并且对读者是谁有明确的立场。如果 Keep a Changelog 那些比较松散的地方正是你团队一直在争论的内容,那么这份规范值得一读。
能不能在不倾倒 git log 的情况下自动化 Keep a Changelog
可以:从结构化的提交推导出草稿,把它放进 Unreleased 里并预填好类型,并要求人在发布前编辑措辞。规范的警告针对的是最终结果,而不是工具本身。从提交推导草稿没问题,未经编辑就直接发布这份草稿,才是它所反对的事情。
机器负责收集和排版,这是它擅长的。人负责挑选和措辞,这不是机器擅长的。Conventional commits 讲的正是这套推导所依赖的两层拆分,以及哪种提交类型该映射到上面六种分类里的哪一种。我们的体验日志工具汇总涵盖了收集这一半所需要的东西。
Keep a Changelog 会在哪里变得不够用
它止步于分发环节。Keep a Changelog 对”这份文件应该长什么样”给出了一个好答案,但对”我们的用户如何得知发生了什么变化”却没有给出答案,因为仓库里的一份 Markdown 文件,只有在你的用户就是贡献者时才是一种有效的分发策略。
这正是大多数团队第二个会撞上的缺口:文件本身没问题,只是团队之外没人读它。要解决这个问题,就意味着条目必须变成能够渲染到别处的数据,这和排版一份文件是完全不同的问题,这也是为什么体验日志范例收集的是公开的体验日志页面,而不是仓库文件。如何把这些条目变成人们会真正回来查看的东西,如何搭建体验日志页面一文有详细说明。
不管怎样,还是采用这份规范吧。它只需要花上一个下午,能让第二个问题变得可以处理,而且它至今仍是关于这个主题写得最好的一页纸。
FAQ
Keep a Changelog 是一个标准吗? 它是一种被广泛采用的惯例,而不是某个标准组织制定的规范。各种工具(发布脚本、代码检查工具、解析器)足够频繁地假设它就是这种形状,所以遵循它能换来兼容性。
Unreleased 小节里应该放什么? 放每一个已经合并、但还没有在一个有编号的发布中出现过的变化对应的条目。当一次发布被切出来时,这个小节会被重命名为版本号和日期,一个全新的空 Unreleased 小节会出现在它上方。
体验日志应该使用语义化版本号吗? Keep a Changelog 推荐使用,但并不要求。库和 API 能从中受益;持续部署的服务通常改用日期,这种格式也能容纳这种做法。
安全修复应该在公开之前就出现在体验日志里吗? 在修复上线时就添加这条条目,只包含足够让运维人员采取行动的细节,不要更多。把条目推迟到统一披露日期是正常的;完全省略它则不是。
本文的技术内容未经独立审核。如有错误,请告诉我们,我们会更正。