关于 writing-release-notes
面向需要决定是否升级的读者,指导编写软件发布说明。它将代码变更转化为读者可见的影响,区分发布说明、变更日志与升级指南,并突出不兼容变更及所需操作。
主要功能
- 01以读者影响描述变更
- 02区分发布说明、变更日志和指南
- 03按类别整理变更
- 04突出不兼容变更和必要操作
- 05采用带版本和日期的格式
适用场景
- 01起草软件版本发布说明
- 02解释 API 不兼容变更
- 03将提交记录转化为读者影响
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ osmontero/ opencode-skills --skill writing-release-notes安装后在 Claude Code 中输入/writing-release-notes确认已加载。
详细介绍
编写发行说明
概述
默认的失败做法是直接粘贴提交日志。它准确、完整,却毫无用处——因为它描述的是代码发生了什么变化,而读者需要知道这对自己意味着什么。
**核心原则:**读者要做两个决定——要不要升级,以及升级后会有什么问题。每一行要么服务于其中一个决定,要么就是噪音。
**相关技能:**面向组织内部受众的 writing-internal-comms,用于编写条目所链接文档的 coauthoring-docs,以及当发行版本包含破坏性变更时使用的 evolving-apis-and-schemas。
铁律
EVERY ENTRY STATES WHAT CHANGED FOR THE READER, NOT WHAT CHANGED IN THE CODE
Refactored the auth middleware to use the new token store 是提交消息。Sessions now survive a server restart 是发行说明。同一个变更,只有后者回答了读者关心的问题。
如果某个条目没有读者可感知的影响,就完全不要将其写入发行说明。提交日志已经存在,点一下就能查看。
第 1 步:明确你在写哪种文档
有三种不同的文档都被称为“发行说明”。把它们混为一谈,对谁都没有好处。
| 文档 | 受众 | 形式 |
|---|---|---|
| CHANGELOG | 追溯历史的所有人 | 简洁、累积,每项变更占一行,按类型分组,最新版本在前 |
| 发行说明 | 正在决定是否立即升级的人 | 先列重点,再列完整清单。重点内容使用散文形式 |
| 升级指南 | 已经决定升级的人 | 仅包含破坏性变更,每项都列出变更前后的情况和操作步骤 |
小型库只需要 CHANGELOG。包含破坏性变更的发行版本需要这三种文档,发行说明应链接到升级指南,而不是将其内容直接写入其中。
第 2 步:收集,然后转换表述
git log --oneline v1.4.0..HEAD
git log v1.4.0..HEAD --format='%s%n%b' | grep -iE 'BREAKING|deprecat'
git diff v1.4.0..HEAD --stat -- '*.proto' 'migrations/' 'openapi.yaml'
最后一条命令最重要:它能找出提交主题通常不会提到的接口变更。
然后用一个问题来转换每个候选条目的表述:**读者现在能做什么以前做不到的事,或者现在有什么行为会发生变化?**如果答不上来,就删掉它。
第 3 步:分类
按以下六类顺序排列,空类别则省略。破坏性变更始终放在最前面,无论相较于其他变更它们看起来有多微小。
| 类别 | 包含内容 |
|---|---|
| 破坏性变更 | 需要读者更改代码、配置或数据的任何内容 |
| 新增 | 新功能 |
| 变更 | 发生变化但保持兼容的现有行为 |
| 弃用 | 仍可使用,但将被移除——注明移除版本 |
| 移除 | 已移除且此前已弃用的内容 |
| 修复 | Bug,按读者遇到的症状来描述 |
| 安全 | 始终单独成节,始终靠近顶部,绝不能埋在“修复”中 |
**弃用信息需要注明日期或版本,不能只写“很快”。**没有移除目标版本的弃用通知会被忽视,之后移除时就会让人措手不及。
**安全条目应说明严重程度,以及是否需要采取行动。**如果已分配 CVE,请将其列出。如果利用漏洞需要特定配置,请说明是哪种配置——读者需要知道自己是否受到影响,而不只是知道应该升级。
第 4 步:编写条目
每条都要说明:变更内容、影响对象,以及详细信息链接。在 CHANGELOG 中写一行;主要功能则写两三句话。
| 不要这样写 | 应这样写 |
|---|---|
Fix null pointer in handler | 修复了上传无扩展名文件时发生的崩溃 |
Bump deps | 已更新至 OpenSSL 3.2.1,解决了 CVE-2024-XXXX(无需采取任何操作) |
Add caching layer | 对于包含 10k+ 份文档的工作区,仪表板查询现在可在 200ms 内返回 |
Refactor config parsing | (省略——对读者不可见的效果) |
Change API response | 破坏性变更: GET /documents 现在返回 items 而非 results。升级前请更新客户端;详见升级指南 |
Improve performance | 导入 100MB CSV 的速度提升约 4 倍(参考数据集上从约 90s 降至约 22s) |
最能体现质量的规则:
- 先说效果,不要先说实现机制。 读者关心的是是否需要重启,而不是令牌存储。
- 给出量化数据。 “更快”无法验证;“导入 100MB 数据从 90s 降至 22s”是可核实的说法,也更有说服力。
- 修复错误时说明症状。 读者会用自己遇到问题时的说法,在发行说明中查找相关信息。
- 说明是否需要采取操作。 大多数条目都不需要;需要操作的条目必须格外醒目。
- 提供链接,不要在条目中塞入细节。 迁移步骤、基准测试和 API 详情应放在链接的文档中。
- 使用过去时、主动语态,不要营销腔。 “会话现在在重启后仍会保留。”而不是“我们很高兴地宣布,焕然一新的会话体验震撼登场。”
第 5 步:为破坏性变更提供迁移步骤
如果条目只说有变更造成了破坏,却不说明该怎么做,那这条目就不完整。
### Breaking
**`GET /documents` renames `results` to `items`**
The list envelope now matches every other collection endpoint.
Before:
```json
{ "results": [...], "total": 42 }
```
After:
```json
{ "items": [...], "total": 42 }
```
**Migration:** update clients to read `items`. The old field is still present
in 2.x and will be removed in 3.0. Clients reading `results` continue to work
until then, with a `Deprecation` header on every response.
最理想的情况是,迁移操作可以用一条命令完成——npx my-tool codemod v3——并且明确写出这条命令。其次是提供差异。最差但还能接受的情况是用文字说明。“用户应更新其集成”不属于以上任何一种。
格式
## [2.3.0] — 2026-03-14
### Security
- Fixed a path traversal in the archive importer that allowed writing outside
the workspace directory (CVE-2026-XXXXX, high). Affects 2.0–2.2 with imports
enabled. **Upgrade recommended.**
### Breaking
- `GET /documents` returns `items` instead of `results` ([upgrade guide](./UPGRADE.md#23))
### Added
- Full-text search across document bodies, with `search:` filters
- `--dry-run` on `import`, printing what would change without writing
### Changed
- Dashboard queries return in under 200ms for workspaces above 10k documents
- Default session lifetime is now 30 days (was 7); override with `SESSION_TTL`
### Deprecated
- `results` in list responses — removed in 3.0
- `POST /documents/bulk` — use `POST /documents` with an array; removed in 3.0
### Fixed
- Uploading a file with no extension no longer crashes the worker
- Timestamps in exports use the workspace timezone rather than UTC
**Full diff:** [v2.2.1...v2.3.0](https://github.com/org/repo/compare/v2.2.1...v2.3.0)
日期使用 ISO 格式,版本号放在方括号中——这种组合能让 CHANGELOG 被工具以固定方式解析,下游工具也依赖这一点。
版本信号
版本号本身也是信息的一部分。按照 semver,破坏性变更意味着主版本号递增——在次版本中发布破坏性变更,比延迟发布、按规范处理更损害信任。如果项目尚未达到 1.0,或采用其他版本方案,请在 CHANGELOG 顶部说明,让读者能够正确判断。
常见错误
直接粘贴提交日志。 内容详尽、按时间排序,却难以阅读,还会把内部命名暴露给根本不知道 ToolGateway 是什么的人。
把破坏性变更埋在列表中间,理由是它只是一个很小的差异。差异大小与对读者的影响无关。
遗漏没人报告的修复。 有人遇到了问题,却从未提交报告。发行说明能让他们知道问题已修复。
“修复了各种错误并进行了改进。” 这句话毫无信息量,读起来就像没人愿意认真编写文档的发行说明。
使用内部术语。 模块名称、工单编号和服务名称,对团队以外的人毫无意义。
逐条列出依赖项升级。 应将它们合并列出,除非其中某项涉及安全修复或行为变更——这种情况应单独列出。
没有日期。 读者会根据日期判断自己正在运行哪个版本。
发布标签时凭记忆编写。 应在进行变更时就添加条目,此时对读者可见的效果仍然一目了然。