固化三件事:入口命令、验收标准、失败处理。第三件最常被省,也最影响别人。 工作流会折旧:十周四个腐烂点,完全不管第三周就废,且再也恢复不了。 同样四次维护,当周修十周全过,拖一周只过六周。差别不在次数,在时机。
上一节讲「谁来做」,这一节讲「做一次之后怎么留住」。 工作流的标准不是「跑通过一次」,是「换个人、隔三个月还能跑通」。 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 秒实验
- 把你的工作流写成三行:入口命令、验收标准、失败先看哪里。 第三行写不出来,说明你还没想好它失败时该看哪。
- 拿上一次「一次就过」的任务,问第二问:换个输入还灵吗。 答不上来,那次就是手气。
- 每周跑一次入口命令,记录日期。 连续四周都过,这条流程才算稳。
常见误区
- 「跑通一次就写成文档。」 一次成功往往是因为输入刚好合适。 先问第二问,再决定要不要写成文档。
- 「流程坏了再修。」 demo 里攒一周再修,同样四次维护只过六周。 坏了再修的成本不是修的时间,是断档的时间。
- 「失败处理就是写「如有问题联系我」。」 那不是失败处理,那是把问题转给你自己。 失败处理要写「先看哪里」,比如「先看 data 目录名是不是改了」。
自测三问(点开对答案)
1. 固化三件事里,为什么「失败处理」最常被省? —— **因为写它的前提是「想过它会怎么坏」。** **写入口命令只需要回忆成功路径,写失败处理要预演失败路径。** 2. 「立刻修」和「攒一周修」维护次数一样,为什么通过周数差这么多? —— **因为攒着修的那一周是断的。** **四次腐烂造成四次断档,断档不修补次数多少。** 3. 四个腐烂点为什么都设计成「只改名字」? —— **因为真实项目里最常见的就是改名和挪目录,而不是 API 破坏性变更。** **改名不改行为,所以最难从现象反推原因。**最后两节收尾:配套仓库怎么用、模板怎么改,以及书过时了怎么办。方法比工具活得久。
- ☐ (本篇完,下一篇 14.5 配套仓库、模板与在线更新机制)