🛠️ 第14章 Codex 的边界、风险与工程方法论
📄 一句话概括

固化三件事:入口命令、验收标准、失败处理。第三件最常被省,也最影响别人。 工作流会折旧:十周四个腐烂点,完全不管第三周就废,且再也恢复不了。 同样四次维护,当周修十周全过,拖一周只过六周。差别不在次数,在时机。

📝 本章和前面几章的关系

上一节讲「谁来做」,这一节讲「做一次之后怎么留住」。 工作流的标准不是「跑通过一次」,是「换个人、隔三个月还能跑通」。 Demo 造一份会失败的流程文件,看三版各自给出什么; 再真跑十周四个腐烂点,比较三种维护方式的通过周数。

一、固化三件事

「固化」不是写文档,是写三样东西。

要固化的 写成什么 缺了会怎样
入口命令 一行能复制的命令 每次都要问「怎么跑」
验收标准 一条命令,退出码即结论 跑完了不知道对不对
失败处理 出错先看哪里、找谁 卡住就停,等人来救

三件事里第三件最常被省,省它的后果最直接:流程一报错,接手的人只能来问你。 这就没省下沟通成本,只是把沟通挪到了出错的时候。 Demo 用三版流程文件跑同一件会失败的事。 v1 只有命令,报错后停住;v2 加了验收,报错时说明「验收未过」; v3 再加失败处理,报错时直接指出「先看 data 目录名」。 三版跑的都是同一件失败的事,差别不在能不能跑通,而在失败时接手的人知不知道下一步。 这就是「失败处理」这一行值钱的地方:它把「来问你」变成了「自己往下走」。 一个判断标准:如果这条流程只有你能跑,它就不是工作流,是你的手艺。

二、复盘要问的四个问题

一次成功之后,最该做的是复盘,但复盘最容易流于「这次挺顺」。 四个问题能把它拉回来。

问题 它想抓什么 答不上来说明
这次为什么成功 触发条件说清了吗 下次照样不会
换个输入还灵吗 有没有过拟合到这一个例子 这是一次手气
哪一步是运气 哪些是偶然成立的 把偶然当成了必然
下次怎么少一步 有没有可以固化的动作 原地不动

第二问最容易被跳过,也最值钱。 第 10 章那个「一轮就过」的 bug 修复,换成另一个输入就未必过—— 当时之所以过,是因为那几行刚好在同一屏里。 这是运气,不是能力。 第四问的答案通常就是「把刚才手动做的那步写成命令」。 工作流是这么一条条长出来的,不是一次设计出来的。 四问的顺序也有讲究:先问「为什么成功」,再问「换个输入还灵吗」。 反过来问会变成「这次有什么问题」,那就跑偏到挑刺上了。

三、工作流的折旧

工作流不是写完就定住的。依赖会改版本、文件会改名、同事会挪目录。 Demo 把这个过程真跑了一遍:十周,四个腐烂点。

第几周 环境怎么变
第 3 周 util.py 改名 helpers.py
第 5 周 data/ 改成 dataset/
第 7 周 load() 改名 read()
第 9 周 check() 改名 check_strict()

四个变化都不改行为,只改「怎么称呼」。 这正是最阴的一类腐烂:逻辑没坏,名字坏了。

维护方式 通过周数 维护次数
完全不管 2/10 0
坏了立刻修 10/10 4
攒一周再修 6/10 4

第一行第三周就废了,而且之后再也没恢复——因为坏的是名字,不是逻辑,光看报错看不出该改哪。 后两行才是重点:维护次数一样,都是四次,通过周数差四。 差别不在修几次,在什么时候修:当周修是全过,拖一周就是四次断档。 所以「定期体检」这件事的价值不在查出多少问题,在把断档压到零。 每周跑一次入口命令,一分钟,能挡掉「三个月后全废」这种最坏结果。 最能查出「文档缺哪」的是每季那一项:让一个没参与的人,只给文档和仓库,跑一次; 卡住的地方就是文档缺的地方。 这跟 13.4 的验收是同一条。

四、动手 Demo:从一次成功到长期可复用:工作流会折旧

先看三版流程文件跑同一件失败的事,各自给出什么。 再真跑十周四个腐烂点,比三种维护方式的通过周数。 腐烂点是我设计的,但那三十次运行是真跑的。

STEP_TMPL = """import os, sys
HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, HERE)
import {mod}
rows = {mod}.{fn}(os.path.join(HERE, "{dat}"))
ok = {mod}.{ck}(rows)
sys.exit(0 if ok else 1)
"""

ROT = [
    (3, "util.py 改名 helpers.py", "mod", "helpers"),
    (5, "data/ 改成 dataset/",     "dat", "dataset/input.txt"),
    (7, "load() 改名 read()",      "fn",  "read"),
    (9, "check() 改名 check_strict()", "ck", "check_strict"),
]

实跑输出:

--------------------------------------------------------------------
从一次成功到长期可复用:工作流会折旧
--------------------------------------------------------------------

  跑通一次不算工作流。工作流的标准是:换个人、隔三个月,还能跑通。

  ① 固化三件事
  --------------------------------------------------------------------
| 要固化的 | 写成什么 | 缺了会怎样 |
|---|---|---|
| 入口命令 | 一行能复制的命令 | 每次都要问「怎么跑」 |
| 验收标准 | 一条命令,退出码即结论 | 跑完了不知道对不对 |
| 失败处理 | 出错先看哪里、找谁 | 卡住就停,等人来救 |

  三件事里第三件最常被省。省掉它的直接后果是:流程一报错,接手的人只能来问你——这就没省下沟通成本。

  ② 三版流程文件,同一件事能不能跑通
  --------------------------------------------------------------------
  造一份 flow.md 和一个会失败的步骤,看三版各自给出什么。
| 版本 | 跑出来 | 用户看到什么 |
|---|---|---|
| v1 只有命令 | err | 报错后停住 |
| v2 命令加验收 | err | 报错并说明「验收未过」 |
| v3 再加失败处理 | err | 报错并指出「先看 data 目录名」 |

  三版跑的都是同一件会失败的事。差别不在能不能跑通,而在失败时接手的人知不知道下一步。

  ③ 折旧:十周四个腐烂点,它自己就坏了
  --------------------------------------------------------------------
  工作流不是写完就定住的。依赖会改版本、文件会改名、同事会挪目录。
  这一节把这个过程真跑一遍:十周,四个腐烂点。
| 第几周 | 环境怎么变 |
|---|---|
| 第 3 周 | util.py 改名 helpers.py |
| 第 5 周 | data/ 改成 dataset/ |
| 第 7 周 | load() 改名 read() |
| 第 9 周 | check() 改名 check_strict() |

  四个变化都是真实会发生的那种:改名、挪目录、重构函数名。
  它们的共同点是不改行为,只改「怎么称呼」。
| 维护方式 | 通过周数 | 维护次数 |
|---|:---:|:---:|
| 完全不管 | 2/10 | 0 |
| 坏了立刻修 | 10/10 | 4 |
| 攒一周再修 | 6/10 | 4 |

  「完全不管」第三周就废了,而且之后再也没恢复——因为坏的是名字,不是逻辑,光看报错看不出该改哪。
  「坏了立刻修」十周全过,修了四次。「攒一周再修」也修了四次,只过了六周。
  两组数字的差别不在修几次,在什么时候修:同样四次维护,当周修是十周全过,拖一周就是四次断档。
  ! 这四个腐烂点是我按常见情况设计的,周数也是设的。
  能看的不是「十周」这个数,是两种维护方式的差别:修的次数一样,恢复的代价不一样。

  ④ 复盘要问的四个问题
  --------------------------------------------------------------------
| 问题 | 它想抓什么 | 答不上来说明 |
|---|---|---|
| 这次为什么成功 | 触发条件说清了吗 | 下次照样不会 |
| 换个输入还灵吗 | 有没有过拟合到这一个例子 | 这是一次手气 |
| 哪一步是运气 | 哪些是偶然成立的 | 把偶然当成了必然 |
| 下次怎么少一步 | 有没有可以固化的动作 | 原地不动 |

  第二问最容易被跳过,也最值钱。
  第 10 章那个「一轮就过」的 bug 修复,换成另一个输入就未必过——当时之所以过,
  是因为那几行刚好在同一屏里。
  第四问的答案通常就是「把刚才手动做的那步写成命令」。
  工作流是这么一条条长出来的,不是一次设计出来的。

  ⑤ 折旧的处置:定期体检
  --------------------------------------------------------------------
| 多久 | 查什么 | 大概花多久 |
|---|---|---|
| 每周 | 那条入口命令还能跑吗 | 一分钟 |
| 每月 | 依赖有没有大版本 | 十分钟 |
| 每季 | 新人只靠文档能不能上手 | 半天 |

  第一行只要一分钟,但它能挡掉「三个月后全废」这种最坏结果。
  第三行是唯一能查出「文档缺哪」的办法。让一个没参与的人,只给文档和仓库,跑一次;
  卡住的地方就是文档缺的地方——这跟 13.4 的验收是同一条。

三版流程文件那张表,三次都跑出 err。 这一点很重要:失败处理不是让失败变成成功,是让失败变成「有下一步」。 很多流程文档写不下去,就是想在这一步把失败藏掉。 折旧模拟里最反直觉的是「攒一周再修」这一行。 它也修了四次,维护成本和「立刻修」完全一样,但只过了六周。 四次断档不是因为多花了时间,是因为每次都要先想「哪里坏了」。 「完全不管」那行第三周就废,之后再没恢复。 原因是四个腐烂点里第一个就是改名—— 改名不会让逻辑出错,只会让 import 失败,而失败信息里没有「该改哪」。 四个腐烂点都不改行为,只改称呼,这是刻意设计的。 因为真实项目里最常发生的就是这种:有人重构了函数名,有人挪了目录,有人统一了命名。 没有一次是「依赖升级导致 API 变了」那么显眼。 每周一分钟那条建议不是修辞。 入口命令跑一次,成功就是一行输出,失败就是当场知道。 这一分钟买到的是「永远不超过七天就发现」。

30 秒实验

  1. 把你的工作流写成三行:入口命令、验收标准、失败先看哪里。 第三行写不出来,说明你还没想好它失败时该看哪。
  2. 拿上一次「一次就过」的任务,问第二问:换个输入还灵吗。 答不上来,那次就是手气。
  3. 每周跑一次入口命令,记录日期。 连续四周都过,这条流程才算稳。

常见误区

  1. 「跑通一次就写成文档。」 一次成功往往是因为输入刚好合适。 先问第二问,再决定要不要写成文档。
  2. 「流程坏了再修。」 demo 里攒一周再修,同样四次维护只过六周。 坏了再修的成本不是修的时间,是断档的时间。
  3. 「失败处理就是写「如有问题联系我」。」 那不是失败处理,那是把问题转给你自己。 失败处理要写「先看哪里」,比如「先看 data 目录名是不是改了」。
自测三问(点开对答案) 1. 固化三件事里,为什么「失败处理」最常被省? —— **因为写它的前提是「想过它会怎么坏」。** **写入口命令只需要回忆成功路径,写失败处理要预演失败路径。** 2. 「立刻修」和「攒一周修」维护次数一样,为什么通过周数差这么多? —— **因为攒着修的那一周是断的。** **四次腐烂造成四次断档,断档不修补次数多少。** 3. 四个腐烂点为什么都设计成「只改名字」? —— **因为真实项目里最常见的就是改名和挪目录,而不是 API 破坏性变更。** **改名不改行为,所以最难从现象反推原因。**

最后两节收尾:配套仓库怎么用、模板怎么改,以及书过时了怎么办。方法比工具活得久。

相关笔记