# Writing For Readers

> why-not-what：单向沟通只记录「为什么」，不复述「做了什么」——注释、README、提交信息。 Use when the user asks to write, polish, or rewrite comments, a commit message, a README, or a PR description; wants code decisions explained for future readers; or when code-review or contributing-upstream needs the commit-hygiene contract. 触发于「帮我写提交信息/注释/README/PR 描述」或补全改动动机。

- Skill: `anthealaffy/writing-for-readers` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add anthealaffy/writing-for-readers`
- Raw SKILL.md: https://api.skillmd.com/api/skills/anthealaffy/writing-for-readers/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: AntheaLaffy (https://skillmd.com/u/anthealaffy)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/anthealaffy/writing-for-readers

---


# 写给读者的单向沟通

读者是没有你当前上下文的人：后来的队友、接手的维护者、半年后的你。一个优秀软件工程师大约一半功力在写代码，另一半在与他人沟通——本 skill 管「写给别人读」的那一半。这类写作只回答一个主线问题——**为什么**（why），而不是做了什么（what）。做了什么看代码和 diff 就懂；为什么才是来之不易、最容易随时间流失的知识。这条主线叫 **why-not-what**：`code-review` 与 `contributing-upstream` 在交接点都引用这里的契约。

## 操作契约

- **先问，不猜。** 写 commit、注释或 README 前，若不知道改动背后的动机、取舍与遗留问题，先向用户补齐上下文。commit 场景必问四件事：① 什么问题或约束迫使这次改动？② 考虑过哪些备选方案、为何选这个？③ 取舍与影响是什么？④ 有哪些读者会意外的点？用户答不上来就标注「作者未说明」，绝不编造动机。
- **详略与复杂度匹配。** 一行错别字只需要主题行；修一个排查数小时的竞态条件，值得用段落解释问题与解法。显而易见的段落可以省略（改一个 off-by-one，就没有「备选方案」可写）；但再平凡的改动，也别只剩一句「fix foo」——零信息量的提交等于没写。
- **克制，尊重读者。** 读者面对太多文字时，一个字都不会读；写作前自问：如果我是读者，我会读吗？解释为什么，相信读者会为自己的情境推出怎么做。用 LLM 生成时尤其要约束它——LLM 擅长批量产出文字，务必要求「精炼的总结，不是长文」。
- **提交拆得语义清晰**（`git add -p`）：一个提交 = 一个可独立理解、独立评审的连贯改动。重构不与新功能混提，无关 bug 修复不塞进同一个提交。LLM 也可以帮你把大 diff 按语义切成多个提交，但切完要自己检查一遍。
- **复杂改动升级盘问。** 背景盘根错节时，改用 `/grilling` 逐轮深挖上下文，而不是让用户一次讲完。

## 注释：写代码本身表达不了的内容

好注释解释「为何这样做」，而不是「如何工作」——代码已经展示了过程。值得写的注释类型：

- **TODO**：留足上下文——还缺什么、为什么延期。写在代码里而不是只放 issue tracker，好处是后来者可以直接 grep 到。「TODO: optimize」毫无价值；「TODO: 这段 O(n²) 循环在 n<100 时没问题，规模放大后需要索引」可行。
- **参考资料**：实现论文算法、借鉴外部代码或遵循文档规定行为时，给永久链接，并注明与参考实现的差异。
- **正确性说明**：解释为什么不寻常的代码能产生正确结果。代码展示步骤，注释说明步骤为何奏效。
- **血泪教训**：花了 30 分钟以上才调通、修复方式不明显——记下来。过去的你不知道需要这一步，未来读者也不会知道。
- **常数的理由**：魔法数字也要解释。为什么是 1492？随手选的、测试得出的还是正确性所需？即便「随意选的」也是有用信息。
- **承重细节**：正确性依赖某个看似无关的实现细节（如「必须是 BTreeSet，因为下面迭代顺序有要求」）——务必点出来。
- **「为什么不用」**：刻意避开显而易见的做法时说明理由。典型场景：这里本该用标准库的 hash map，却用了别的结构——不写清楚，某位聪明工程师会把它「修」回标准库，然后重走你踩过的坑。

复述代码的注释是噪音，甚至误导读者——不写。

## README：像漏斗一样组织

按顺序回答四个问题：**它做什么？我为何要在乎？如何使用？如何安装？** 顺序很重要——先展示用法、后讲安装，人们想先看到能得到什么，再决定投入安装步骤。顶部一句话描述（可加视觉示意），让人几秒内判断是否解决自己的问题；整体保持精炼、可扫读（skimmable），不要变成什么都往里塞的杂物袋。README 面向**使用者**；面向**协作者**的内容（bug 报告流程、PR 流程、测试方式、代码约定）放 CONTRIBUTING.md，不占 README。

## 提交信息：记录「为什么」的历史

提交信息构成代码库演进史。有人（包括你）跑 `git blame` 弄懂某处困惑改动时，它要能回答：什么问题迫使我们改动？考虑过哪些备选方案？取舍与影响是什么？有哪些可能令人意外的点？复杂改动采用「**问题 → 解决方案 → 影响**」结构；最后一部分尤其重要——记录某个取舍是刻意的，防止后人以为你忽略了问题。取舍常藏在非显而易见处：比如让运行时更快、却让编译更慢（C/Rust 这类区分构建期与运行期的语言尤其如此）——这类影响一定要写，因为看 diff 看不出来。

### 为什么值得做：git bisect

好提交信息 + 语义干净的提交直接决定 `git bisect` 的威力：二分定位「哪个提交引入 bug」时，如果命中的提交信息为零、或 diff 混着一堆无关改动，定位了也白搭。若能用测试脚本复现 bug，`git bisect run <script>` 还能全自动跑完。这是「提交拆干净」最实际的回报之一。

### 用 LLM 写提交信息

直接把 diff 丢给 LLM，它只能看到「做了什么」、看不到「为什么」，产物是描述性的——与需求正好相反。正确做法：在**用 LLM 协助改动的同一个会话**里写提交（对话天然包含上下文）；或者把 diff、本节的写作要求一起给它，并附一句「缺上下文就问我」——让它把你当作读取上下文的「工具」，做成互动式问答。

## 练习

学习材料在 `exercises.md`。

> 改编自 MIT The Missing Semester 课程 Lecture 8: Beyond the Code（讲义 + 口播稿，CC BY-NC-SA 4.0）：https://creativecommons.org/licenses/by-nc-sa/4.0/ · 课程站点：https://missing.csail.mit.edu/ · 讲座视频：https://www.youtube.com/watch?v=2DOEATfXT8k

