同一篇文档,读者会有三种不同的期待:看懂这件事是什么、照着做一遍能成功、以及知道什么时候不该这么做。这三种期待对应三个层次,缺任何一层,文档都会在某个时刻失效——通常是读者最需要它的时候。
第一层:能看懂
看懂的最低门槛是:读者不需要另外开一个搜索框。
两条具体规则:
术语先定义。 作者熟悉的概念对读者常常是陌生的,而且不会主动说”我没懂”。出现专业术语时,用一句话给出可工作的定义,而不是给出一个名词解释。
不够:"这里涉及幂等性。"
够用:"幂等性指同一请求重复执行多次,结果与执行一次相同。
本文场景中,指同一条消息被消费两次不会重复扣款。"
第二种把定义落到了本文语义上,读者能直接往下读。
给出最小示例。 抽象描述之后立刻跟一个能看见的东西:一个输入与输出、一小段代码、一个具体数字。最小示例的作用是把概念锚定住,让读者后续遇到”这个参数会怎样”时能自己推。
讲清楚"读已提交"与"可重复读"的差异:
事务 A 开启,读 x → 得到 1
事务 B 修改 x = 2 并提交
事务 A 再读 x:
读已提交 → 得到 2(能看到别人已提交的修改)
可重复读 → 得到 1(始终看到事务开始时的一致性快照)
一段示例胜过三段解释。最小示例合不合格的标准:读者只看示例能否猜出结论。
第二层:能照做
能看懂和能照做之间有巨大的鸿沟,鸿沟里装的是那些作者没写、因为他自己环境里恰好没问题的前提。
能照做的文档要写清四样东西:
1. 环境前提:版本、操作系统、依赖服务、权限
2. 可复现的步骤:每一步有明确的输入与预期输出
3. 失败时怎么办:常见错误的现象与对应处理
4. 验证方法:做完怎么确认成功了
第 3 项最常被省略,也最影响信任度。读者卡住时若文档只有正确路径,他只能放弃或去搜。补一节”常见失败”:
| 现象 | 可能原因 | 处理 |
|---|---|---|
| 连接被拒 | 目标服务未监听该端口 / 只监听 127.0.0.1 | 确认监听地址与端口,检查防火墙 |
| 认证失败但密码正确 | 两端协商出的认证方式不一致 | 核对两端的认证配置与协议版本 |
| 命令执行成功但无效果 | 作用于错误的实例/命名空间 | 打印当前上下文再执行 |
第 4 项(验证方法)同样关键。每条步骤配一句”执行后应看到 X,若看到 Y 说明 Z”,读者能自我校验,不必回来问作者。
检验方法:把文档交给没做过的同事,不做口头补充,看他能否独立完成。他卡住的每一点都是缺失的前提。
第三层:能判断
最高一层不是讲得更细,而是讲清边界:方案在什么条件下成立,超出后会怎样,此时该换成什么。
三个必写项:
适用边界:规模、并发、数据特征、团队能力上的限制
失效征兆:出问题时最先表现在哪个指标或现象上
替代路径:超出边界后往哪个方向走,为什么
举例,讲”用数据库表做任务队列”这个常见做法,第三层应该写:
适用:日任务量十万级以内,单表行数控制在千万级以内,可接受秒级延迟
失效征兆:扫描任务的 CPU 占用持续升高;积压行数在业务低峰也不下降
原因:轮询扫描的代价随表行数增长,索引在高频更新下维护成本上升
替代:任务量到百万级、或要求毫秒级延迟时,改用专用队列中间件,
迁移点是生产端与消费端接口,任务表结构可以保留做兜底
没有这一层,读者会把方案当成通用解,用在超出边界的地方,然后归咎于文章没讲清楚。文章讲的是”怎么做”,判断”该不该这么做”是读者的责任,但作者有责任把判断所需的信息给全。
类比:好用,但要标出失效点
类比能把陌生概念接到读者已有经验上,效率很高。代价是:所有类比都会在边界处失真。
问题不在失真,而在读者会沿着类比继续推理,推到失真处就得出错误结论。所以类比必须配一句”它在哪失效”。
类比:"连接池就像图书馆借书,借了要还,不还别人就没书看。"
有用之处:解释了为什么必须归还连接、为什么池大小决定了并发上限
失效之处:书被借走后别人只能等;而连接超时后可以被强制回收并新建。
另外,图书馆不存在"持有连接期间服务端状态被污染"的问题。
应补的一句:"这个类比只在'资源有限、需要归还'这一点上成立;
连接的超时回收与状态清理,需要另外理解。"
判断一个类比该不该用:如果它解释的恰好是这个概念的核心机制,可以用;如果只是表面像而机制不同,换成直接描述加示例更安全。
删掉”显然””众所周知”
这类词的作用只是暗示”你不懂是你的问题”。后果是读者跳过提问,把疑惑带进后面的段落,然后在某处彻底断掉。
带词的写法:"显然,这里需要加锁。"
改成: "这里需要加锁,因为两个线程可能同时读到同一个计数值,
各自加一后写回,导致一次更新丢失。"
后一种写法多花 25 个字,换来读者不必停下来怀疑自己。
同类需要警惕的还有:”简单地说”(对谁简单)、”大家都知道”、”只需要”(暗示没有坑)、”当然”。共同问题是把作者的知识状态当成读者的知识状态。
更隐蔽的一种:写步骤不写理由。”先执行 X,再执行 Y”若省略了为什么顺序不能反,读者出错时无法自救。
代码在文中的长度
正文里的代码服务于说明,不服务于完整性:
够用:能说明论点的最短片段,通常 5–20 行
过长信号:需要滚动才能看完;包含与论点无关的错误处理、日志、配置
处置:正文保留核心片段,完整可运行版本放附录或仓库链接
处理长代码的几个做法:
- 用注释标出”关键在这一行”,避免读者在几十行里自己找重点。
- 省略无关分支时用
// ...标注,不要默默删掉,否则读者会以为代码不完整。 - 展示差异而不是全量。改一个函数时给 diff,比贴整个文件有效。
- 输出与代码配对,读者不用自己想象结果。
// 关键在这一行:只用 err 判断,未区分超时与其他错误
resp, err := client.Do(req)
if err != nil {
return err // 超时也走这里,无法决定是否重试
}
一份自检清单
写完放一小时再回来看一遍,逐条过:
[ ] 每个专业术语首次出现时,有没有给可工作的定义?
[ ] 每个抽象结论后面,有没有一个最小示例?
[ ] 环境前提、版本、权限写了吗?
[ ] 有没有"常见失败现象 → 原因 → 处理"这一节?
[ ] 每一步有没有可自我验证的预期输出?
[ ] 边界条件、失效征兆、替代路径写了吗?
[ ] 类比有没有配"它在哪失效"?
[ ] 全文有没有"显然/众所周知/只需要"?
[ ] 每段代码是否都短到能一眼看完,长的是否已移入附录?
[ ] 删掉第一段之后,文章是否仍然直接进入主题?
还有一条关于开头。最常见的浪费是开头那段”随着 XX 的发展,XX 变得越来越重要”——它不传递信息,却占了读者注意力最集中的位置。直接写第一件有用的事。
评论(0)
还没有评论,来抢沙发。
请 登录 后参与评论。