一份设计文档在评审会上顺利过了,半年后被投诉三次:新人说看不出从哪进手,接手的人说找不到当时为什么这么选,值班的人在事故里翻了二十分钟没找到"这步失败会怎样"。作者很委屈——这些内容明明都写了。
问题不在写没写,而在一份文档的详细程度只能取一个值。评审者要"为什么不是另一个方案",新人要"第一步敲什么",运维要"这个依赖挂了怎么办"。三者对详细程度的要求相反:评审者嫌浅是因为不需要步骤,新人看不懂是因为缺前置,运维找不到是因为他找的是失败分支而不是主流程。折中成中等详略,三种都不满意,且这不满不会在评审现场暴露——现场只有评审者。
分层不是文风问题,是一笔账:拆成几份就承担几份同步成本;不拆就承担一部分读者永远读不到自己要的东西。这篇只算这笔账。
1. 三种读者要的不是详略程度,是不同的问题
把诉求差异列成表,"折中必然失败"就不再是感觉,而是加法:
| 读者 | 入口问题 | 要的产物 | 能满足他的段落形态 | 详略落在哪 |
|---|---|---|---|---|
| 新人 | 从哪进 | 前置清单、第一条命令、最小可跑路径 | 编号步骤 + 名词表 | 过程要细 |
| 评审者 | 为什么这么选 | 被否决的选项、判据、失效条件 | 选项对比表 | 论证要细 |
| 运维 | 出事怎么办 | 失败分支、观测点、回滚动作、告警含义 | 排错表 + 依赖清单 | 分支要细 |
| 半年后的作者 | 当时的约束是什么 | 时间点、外部限制、临时妥协 | 决策记录 | 背景要细 |
三个方向的"细"落在不同位置,总量是三者之和,而混合文档的篇幅只能等于其中一项。这不是取舍,是加法。
失效方式因此是确定的:折中的结果是新人卡在第二步、评审者跳过论证;以评审者为主,主流程没有可执行步骤;以新人为准,选项与判据被当冗余删掉;全写细则把判断埋进步骤。最后一种最该警惕——全写细是把检索成本转嫁给读者。可用性由"能否在两分钟内找到那一段"决定,不由字数决定。
2. 拆分的三种方式,代价结构完全不同
| 拆分方式 | 结构 | 同步成本 | 适合的读者关系 | 主要失效 |
|---|---|---|---|---|
| 主文档 + 附录 | 一份正文,后面挂实现细节 | 低,同文件 | 评审者为主、新人为辅 | 附录被跳过,运维找不到 |
| 按角色分文档 | 新人版、评审版、运维版各一份 | 高,N 份要同改 | 读者基数大且稳定 | 三份逐渐不同步 |
| 按问题分文档 | 为什么这么选 / 怎么用 / 坏了怎么办 | 中,边界要专人守 | 问题可枚举、读者会主动提问 | 边界模糊处互相推诿 |
成本排序是固定的:按角色分最贵,它把同一段事实复制三份,每次变更要改三处且必然产生语义漂移;主文档 + 附录最便宜,只有一处事实源,只是入口不同;按问题分居中,代价是每份都要独立回答一个完整问题,不能靠"见上文"省字。
三类读者比例接近且各只有两三人时,按角色分是纯亏;只有某一类(通常是新人)持续增加才值。而分层最便宜的落地不是拆文件,是在开头声明服务谁、不服务谁:
// 文档头:把"给谁看"变成可判定字段,而不是作者的默会
doc_id: DESIGN-2026-014
audience_primary: 评审者 // 决定详略的唯一依据
not_for: [新人, 值班运维] // 显式不承诺,强制给出替代入口
entry_points:
- {reader: 新人, path: onboarding/QUICKSTART.md}
- {reader: 运维, path: runbook/SERVICE_OPS.md}
depth_marker: 论证到选项对比层,实现只给约束不给代码
sync_with: [QUICKSTART.md, SERVICE_OPS.md] // 变更时必须同改
not_for 是最值钱的一行:把"新人看不懂"从缺陷改写成已知边界,并逼作者给出替代入口。
3. 最容易被牺牲的读者是运维,因为他们不在评审现场
这是全文最具体的判断。评审会上坐着评审者,作者写的时候想的也是评审者,于是论证部分最细。新人会在入职后抱怨,运维会在事故后抱怨,两类抱怨都不发生在文档被生产的那一刻。
| 读者 | 生产文档时的在场情况 | 反馈到达时间 | 结果 |
|---|---|---|---|
| 评审者 | 当场在场,最强势 | 立即 | 详略被其需求主导 |
| 新人 | 不在场 | 数周 | 表现为"看不懂",被归为新人问题 |
| 运维 | 不在场 | 数月,且发生在事故中 | 表现为"找不到",被归为态度问题 |
修偏差的动作很具体:列出三类读者各自的第一个问题,检查每一类在文中有没有对应段落。这是结构检查,不判断质量:
// 三类读者的入口段落是否存在(只看有无,不看写得好不好)
for s in quickstart why-this-choice failure-modes; do
grep -q "^## .*${s}" DESIGN.md || echo "missing: ${s} // 缺一类读者的入口"
done
// 反向检查:只服务评审者的文档通病——全文没有任何失败分支描述
grep -ciE "rollback|timeout|fail|回滚|超时" DESIGN.md
最容易被跳过的是单页摘要。决策者不会读第二页,所以要单独写一页能独立成立的摘要,判据是"只读这一页能不能拍板":
// 单页摘要:决策者唯一会读的一页,五段以内
结论:选 B(异步补偿),不接受 A 的强一致承诺
判据 1:峰值写入是均值的 X 倍,同步路径会排队 // 按最近一次压测口径核对
判据 2:失败可重试,业务能接受秒级延迟
判据 3:补偿表可复用现有对账基础设施
主要代价:状态可见性延迟到秒级;新增一张补偿表与一个定时任务
失效条件:若业务改为要求实时一致,本结论作废,回到 A
摘要的价值不在"短",在于把决策所需的最小充分信息放在一处。写不出这一页,通常说明结论还没定。
读者比例还会随时间翻转:写的时候只有评审者,半年后只有运维。重心应随读者迁移,但迁移动作没人负责,于是多数文档停在第一行。
| 时间点 | 主要读者 | 应有的侧重 | 常见实际状态 |
|---|---|---|---|
| 设计期 | 评审者 | 选项与判据 | 正确 |
| 上线 6 个月后 | 运维、想改它的人 | 失败分支、约束与妥协 | 仍是设计论证,逐渐无人读懂 |
"运维部分以后补"听起来是延后,实际是把成本从作者转移到事故现场:
分支 A 写文档时补运维段:作者 30–60 分钟(按本人熟悉度)
分支 B 出事时现场重建:值班 20–40 分钟定位 + 一次错误操作的概率
+ 事后补文档 30 分钟(语境已丢失)
+ 可信度折旧:文档被标记"关键时刻靠不住"
代价不是工时,是可信度折旧:一份被验证过靠不住的文档,会让读者在所有文档上退回"自己去看代码"。
4. 同步成本集中在多份文档上,度量只需要四个口径
分层之后最容易出问题的不是写,是改:
| 变更类型 | 需要同步的份数 | 漏改后果 | 降成本机制 |
|---|---|---|---|
| 接口字段改名 | 主文档 + 快速上手 + 排错表 | 按旧名执行 | 字段定义只留一处,其余引用 |
| 依赖组件替换 | 主文档 + 运维依赖清单 | 按旧组件排错 | 依赖清单由配置生成 |
| 失败行为变化 | 排错表 + 告警说明 | 按旧语义处置 | 排错表与告警规则同仓 |
| 约束放松 | 主文档 + 决策记录 | 以为旧限制仍有效 | 决策记录追加,不改写 |
判据一句话:会被代码或配置消费的内容不要手写第二遍。分层成本常被高估,因为把可生成的内容也当成了要手写的副本。
| 指标 | 口径 | 异常信号 | 成本 |
|---|---|---|---|
| 查阅角色分布 | 按读者账号或来源目录统计 | 单一角色占九成以上 | 需埋点 |
| 缺失反馈数 | "内容不在文档里"的记录次数 | 同类缺失持续出现 | 需一个登记处 |
| 同步滞后天数 | 代码变更日到对应文档更新日 | 大于一个发布周期 | 零成本可测 |
| 新人首次自读成功率 | 不提问能否完成一件事 | 低于半数 | 需一次观察 |
其中 同步滞后天数 最容易测也最常被忽略:不需要读者配合,只要把提交时间与文档修改时间对一下:
// 同步滞后:取最近 N 次模块提交,比对文档最后修改时间
git log -n 20 --format='%cs' -- MODULE_PATH > /tmp/code.txt
git log -n 1 --format='%cs' -- DESIGN.md > /tmp/doc.txt
// 相差超过一个发布周期,视为分层失效,优先补入口段而非全文重写
5. 排错:五种"读不出自己要的"现场
| 现象 | 真实原因 | 处理方向 |
|---|---|---|
| 新人读完仍问从哪开始 | 文档以结论开头,无前置与第一步 | 补入口段,或另给一页快速上手 |
| 被追问"为什么不用 X" | 被否决方案没写进文档 | 补选项表,含否决理由与代价 |
| 事故中找不到失败分支 | 只写了正常路径 | 补排错表,映射告警 |
| 三份文档不一致 | 按角色拆分且无单一事实源 | 收敛为一份,其余改为引用 |
| 读者说"过时了" | 无 owner、无复核期 | 加状态行与到期字段 |
6. 边界:什么时候分层是负担
| 情形 | 不分层的理由 | 更合适的做法 |
|---|---|---|
| 团队只有三四人 | 角色重叠,一人兼评审与运维 | 一份文档 + 结论前置 |
| 文档只服务作者自己 | 无第二读者,谈不上分层 | 个人笔记,不设结构 |
| 生命周期短于一个季度 | 读者未迁移,文档先废弃 | 结论 + 关键命令 |
| 内容已被代码或配置约束 | 机器执行比文字可靠 | 一句话指向配置文件 |
判断只需一句:这份文档会在作者离场后,被不同诉求的人分别打开吗? 会,就分层;不会,分层只是把一个读者的需求拆成三份维护成本。
自测
- 为什么"折中详略"必然导致三类读者都不满意?这是取舍问题还是加法问题?
- 三种拆分方式里哪一种维护成本最高,高在哪一步?
- 文档头部
not_for字段解决了什么问题,为什么它比audience更关键? - 文档生产时哪一类读者不在场,这如何改变文档的详略分布?
- 四个度量里哪一个不需要读者配合就能测,怎么测?
- 判断"这个场景不该分层",只用哪一句提问?