22 工程日记
用工程日记保存日期、问题、决定、命令和结果,建立跨时间的外部记忆与决策轨迹。
学习目标
- 能把一次工程事件拆成时间、问题、决定、命令、结果和后续六类可复核记录
- 能修改一个记录条件,找到证据链的首个断点,并说明如何从原始输入重放
- 能回答:当时未知的假设为什么必须和结果一起保存,而不能事后凭记忆补写?
为什么这一单元不可压缩
一次排障、迁移或发布,往往在几小时后只剩几条聊天消息和一个“已经好了”。如果没有当时的输入、选择和结果,下一位工程师无法判断哪些事实真的发生过,也无法知道某个决定是在什么边界下成立的。
本单元把短暂的工作过程改写成一页可回看的记录:先固定发生时间和对象,再写下正在验证的解释,接着记录唯一改变和执行命令,最后保存结果、未知项与下一次复核入口。它解决的不是“写更多文档”,而是让未来的人可以从同一份证据重新走一遍。
先看问题:记忆会把过程压扁
想象你在周五修复一个偶发超时。周一只看到“把超时改小后通过”,却不知道当时使用的版本、触发输入、判断依据和未覆盖的边界。结果看似清楚,过程却无法复查;下一次相同症状出现时,团队只能再猜一次。
先预测:如果删掉“为什么采取这一步”的那行记录,五个交接点中哪里应该最先拒绝继续?打开下面的时间轴,先记下你的答案,再用单步控制核对它。
常见误区
五个字段:让一次决定有时间坐标
问题:先固定要解释的对象
↡按时间保存问题、决定、命令、结果与后续,方便别人重建一次工作的外部记录。 不是流水账,而是给一次工程事件建立外部记忆。第一行应回答“谁在什么时间观察到什么对象”,例如 09:10 / incident-42 / API 超时;没有对象和时间,后面的命令就无法归属。
时间坐标:把先后关系变成证据
↡把输入、动作和结果放在同一时序中,标出先发生什么、后知道什么的记录。 让复核者区分“当时已知”和“后来才知道”。它至少包括事件时间、记录时间、输入版本和时区;如果系统时间不可靠,就补一个可以交叉核对的外部标记。
不要用“上午”“刚才”代替坐标。相对时间会随着读者的位置变化,精确到分钟的顺序则能帮助大家发现:某个决定是否早于结果、一次重试是否被误当成新的实验。
决策理由:保存选择,而不是只保存动作
↡说明为何在当前边界下选择某个动作,以及这个选择准备验证哪个假设。 把“我运行了命令”连接到“我想验证什么”。它应写出候选解释、被选动作、只改变的条件和拒绝条件;理由不需要长,但必须让另一个人能判断这次操作是否回应了原问题。
如果一个动作同时升级依赖、重写配置、扩大样本并改变权限,结果就无法归因。把改变缩成一个变量,暂时不确定的部分明确写成未知,而不是用漂亮的结论填空。
实验记录:把观察和命令绑定
↡保存一次受控尝试的输入、命令、版本、预期、实际输出和失败上下文。 不只复制终端文本。至少保存输入身份、命令摘要、工具版本、预期结果、实际结果、退出状态和输出摘要;敏感值要脱敏,但不能删掉决定复核所需的结构。
命令可以是脚本、查询、测试或人工检查。若命令改变了文件或服务状态,应另外写清变更边界与回退动作,避免下一次重放从一个已经污染的状态开始。
结果与后续:保留可重放入口
最后一栏不仅写“通过”或“失败”。写出结果是否支持原假设、哪个边界仍未覆盖、谁需要复核,以及下一次从哪里继续。这样,日记页既能结束一次工作,也能把未完成的风险交给下一个接收者。
当相同输入、规则和起始状态再次运行时,若能得到可比较的输出与失败位置,就有了 ↡用同一输入、版本和起始状态再次执行,结果可比较且首个分叉可定位的性质。 证据。可重放不是保证永远成功,而是保证失败不会只存在于作者的记忆里。
第 1 / 5 步 · 1. 问题:可追问的对象
沿时间轴观察:哪一条记录让未来的复核者知道当时为何采取这一步。
从记录到可复核的代码结构
下面的结构把“写入日记”放在受控尝试的边界上。先记录预测,再执行一次,最后把实际输出和下一步写回;不要在异常分支里只保留一条“失败”字符串。
const entry = {
at: "2026-08-08T09:10:00+08:00",
input: "incident-42@commit-7c1",
hypothesis: "重试放大延迟",
decision: "只改变超时边界",
command: "replay --case 42 --timeout 800",
expected: "首个差异出现在响应等待",
};
const actual = runReplay(entry);
const result = {
...entry,
actual: summarize(actual),
exitCode: actual.exitCode,
next: actual.ok ? "补充回归样本" : "保存首差并回退",
};
writeDaybook(result);这里的关键不是字段名,而是顺序:entry 在动作前冻结,actual 不能覆盖 expected,next 必须由实际结果决定。真实项目还要补权限、脱敏、持久化失败和并发写入策略;这些边界也应出现在记录里。
三步观察:从事件走到后续
每一步都使用同一个事件样本。动手前先写预期的首差;图中的高亮只表示教学位置,不替你证明真实系统已经满足合同。
1. 固定问题与时间
记录事件身份、观察时间、输入版本和当前状态。选择正常样本、边界样本和一个尚未解释的样本,确保日后能区分新事件与重试。
第 1 / 5 步 · 1. 问题:可追问的对象
沿时间轴观察:哪一条记录让未来的复核者知道当时为何采取这一步。
运行实验:删除一行理由会在哪里断开
猜一猜:删除“决策理由”后,系统会在问题、决定还是操作处先拒绝?点击“注入单故障”,再逐步推进;最后点击“重置实验台”,确认图示和故障状态都回到起点。
第 1 / 5 步:问题 已写入日记。
第 1 / 5 步 · 1. 问题:可追问的对象
先预测首差,再注入故障;重置后应回到第一条记录与未注入状态。
决策账本:让复核者不依赖作者记忆
| 字段 | 必答问题 | 拒绝条件 |
|---|---|---|
| 时间 | 事件何时发生?使用哪一时区和输入版本? | 只有“刚才”或“本周”,无法排序 |
| 问题 | 哪个对象出现了什么可观察异常? | 只有结论,没有原始现象 |
| 理由 | 哪个假设促成了这个动作?只改变了什么? | 同时改变多个变量,无法归因 |
| 命令 | 用什么工具、输入和权限执行?预期是什么? | 只有复制的终端片段,没有上下文 |
| 结果 | 实际输出支持还是反驳假设? | 只写“通过”,不写首差和边界 |
| 后续 | 谁在何时从哪份输入重放或复核? | 没有责任人、入口或未覆盖项 |
一份好记录也应保留失败。失败不是需要擦掉的噪声,而是告诉团队哪一个边界已被尝试、哪一种解释被排除,以及下一次不应重复什么。
本章回顾
- 先固定时间和问题对象,再开始动作。
- 把选择理由与命令绑定,控制一次只改一个条件。
- 结果要包含预期、实际、首差和仍未知的边界。
- 可重放要求输入、版本和起始状态都能被恢复。
- 后续项把未完成风险交给明确的复核者。
完成本章后,你应能用一页记录重建一次关键实验,指出当时未知而后来被证实的假设,并说明下一位工程师如何从原始输入继续。
练习:把流水账改成证据链
练习
问题 1:设计一页记录。 某次接口超时发生在 09:10,工程师运行了一个重放命令,结果从 18 秒降到 2 秒。请列出至少六个字段,使另一位工程师能判断这个结果是否支持“重试放大延迟”的解释。
问题 2:改实验台代码。 把实验台的故障从“删除决策理由”改成“删除输入版本”。请预测首差、应显示的拒绝原因,以及重置按钮必须恢复哪些状态。
问题 3:场景选型。 你有三种工作:一次性手工排查、每天运行的迁移脚本、需要跨团队审查的线上变更。分别说明哪一种记录强度最低、哪两种必须保存可重放入口,并解释原因。
名词解释
名词解释
本章出现的专业名词,用大白话再讲一遍。
- 工程日记
按时间把一次工作中的问题、选择、命令和结果放在同一页,方便别人回看和重建。
- 时间坐标
说明事情何时发生、使用哪个版本和时区的标记,让记录可以正确排序。
- 决策理由
用一句或几句人话说明为什么选这个动作,以及它准备验证什么。
- 实验记录
把一次受控尝试的输入、命令、预期和实际输出放在一起保存。
- 可重放
另一个人在相同输入和起始状态下,可以再次执行并定位差异。
来源与改写范围
- 作者与英文版页面:核对 20 周年版书名、版本与 Topic 22 的目录坐标。
- 中文公开目录:核对“22 工程日记”的中文目录位置。
- Git:git-log 文档:参考时间、版本和历史记录的可追溯边界;本章模型、图示、代码与练习均为独立改写。