跳到主要内容

日常手法 — 从曳光弹到橡皮鸭

这一章讲五件事: 曳光弹与原型怎么分工;为什么一次只做一件事; 橡皮鸭为什么好使;三大美德怎么翻译成日常动作;以及打断的代价。 主走查:同一个「配送中心软件」,原型路线与曳光弹路线各走一遍。

1. 曳光弹:先打通一条会发光的弹道

这一节回答:项目早期最贵的未知数是「整体能不能转起来」——怎么用最小代价把它点亮。

曳光弹(tracer ammunition)借自机枪战术:弹链上每隔几发混装一发会发光的子弹, 射手靠它的光轨修正瞄准——曳光弹和普通子弹同枪同环境发射,它打得中, 意味着普通子弹也打得中1

编程里的曳光弹:先写一条「从输入到输出」端到端(从入口一直到出口、全程贯通)能跑通的骨架, 代码都是正式代码,后续开发不断往里添肉,骨架最终留在成品里2。 书里列的收益,每一条都指向同一个机制——尽早获得真实反馈3: 用户看到能跑的东西才给得出有用的意见;程序员有了发挥的舞台; 测试与调试有了整合平台(当天测完的代码当天合入,影响范围小); 利益相关者随时能看演示;进度以用例为单位,报告不含水分。

主走查:原型与曳光弹各走一遍

同一个项目走两条路线,差异一目了然(场景与原书一致:给配送中心做一个装车软件, 两个难点——界面不好设计、装载算法很难)4:

路线 A:原型路线(为「学习」而生,用完就扔)
第 1 步 用快速搭界面的工具做出界面原型:
画面能动、按钮能按,但按下后功能不必真跑
第 2 步 请终端用户上手试用,收集「使用感觉」
第 3 步 装载算法:做出多个算法原型,对比效果
第 4 步 需求一致、算法定稿后,原型全部抛弃,
按定下的方案重新编写正式代码
产出:正确的需求与算法;原型本身价值归零

路线 B:曳光弹路线(为「骨架」而生,持续长大)
第 1 步 把「简单但真的能跑」的界面和装载算法直接连起来:
代码粗糙,但从选货到装车全流程贯通
第 2 步 用户和程序员都在这条真骨架上工作
第 3 步 每轮迭代往骨架上加功能,简陋处逐步替换成正式代码
产出:骨架留存——「当初创建出来的代码是怎样运行的,
软件就怎样运行」[^5]

图说:书里的总结——原型的用途是学习,价值在教训里;
曳光弹是最小限度的可运行正式代码,是系统最终形态的「骨骼」[^6]。

两者的分工,书里给了一句准确的定位:原型是发射曳光弹之前的「侦查活动」5。 拿不准概念时先打原型学一课;概念清楚后,打曳光弹立骨架。

骨架点亮之后,「真实反馈」还有一道补充工序,原书把它单独列为一条手法:内部测试—— 行话叫 Dogfooding(直译「吃自己的狗粮」):发布之前,开发团队以用户的身份真实使用自己的软件, 不是模仿用户的动作,而是真的拿它干活6。 为什么有效:开发团队对自己做的东西会下意识地回避故障;换成用户身份,才能撞见测试没覆盖到的缺陷, 还能发现哪些功能是多余的、哪些用着不顺手——功能过剩与功能不足,站在程序员的视角都看不见6

2. 一步一步走:混着做,回不了头

这一节回答:日常执行的第一纪律——为什么一次只做一件小事。

One by one:一次只做一件小事;完成、检查、确认没问题,再开始下一件7。 书里给的四条理由,每条都连着一个具体场景8:

  1. 混着做会互相污染:两件事搅在一起,「哪个都处理不好」;
  2. 回滚容易:只做过步骤 N,出问题就倒退到 N-1——书里的反例: 只想删类 A,但 A 和类 B 的改动混在一团,「在不影响类 B 的情况下回滚类 A 就会非常困难」;
  3. 每步可检查:新函数哪怕还没人调用,也能先编译查语法、再跑单元测试查逻辑;
  4. 新旧替换安全:让新旧代码并存运行,新代码能完全替代旧代码时,再一口气删旧—— 「失败的风险要小许多」。

第 4 条就是现代持续交付里「功能开关」的雏形,书里描述了机制但没用这个词。 落到操作,作者给了两个现场口令9:重构时不许顺手改名—— 移动函数是一步,确认能跑后,改名是下一步;TDD(测试驱动开发: 先写测试、再写让它通过的实现,小步循环)就是「一点测试、一点代码」的节奏—— 不许一口气写完所有代码再补测试。

这条原则还有一个心理学的延伸,书里放在「关联信息」处但很有分量:思考也要一步一步来。 聪明人不是跳过中间步骤,而是每一步又快又准,「所以整体看起来才又快又准」10。 想不出答案时不怪脑子,扎实按步骤来;还有一个实操技巧:边写边思考—— 想不明白的地方,写下来一看就明白了11

3. 橡皮鸭调试法:说,是最便宜的检查

这一节回答:卡住的时候,成本最低的下一步动作是什么。

橡皮鸭调试法(rubber duck debugging):把问题或有问题的代码, 讲给一个「能点头但不会说话」的对象听——一只浴缸橡皮鸭就行。 效果常常立竿见影:有时候刚开始说明,自己就注意到了问题所在12

它为什么有效?书里给了两层机制13:

  • 说明之前必须通读代码、把隐藏的假定条件挖出来——这一步本身就在逼近答案;
  • 讲到出问题的代码时,常会发现那里**「无法说明」或「说明的内容与代码不符」**—— 缺陷就藏在你讲不圆的那一段。

书里引了一个真实的制度设计:某大学服务台规定,学生咨询前必须先向柜台边的泰迪熊玩偶 把故障讲一遍——很多问题讲到一半就自己解决了,「该做法取得了很好的效果」14。 组织学上的提醒:同事是最好的听众但时间最贵, 「如果无论如何都要与同事交流,应先说明主旨,取得对方的同意」15

这条手法和第 02 章能连上:讲不出来,往往说明命名或结构撒了谎—— 橡皮鸭是最快的「环回检测器」。

4. 三大美德:懒惰、急躁、傲慢

这一节回答:这套手法的发动机是什么——程序员的三种「反面」品格,各自翻译成正面动作。

程序员的三大美德出自 Perl 语言之父 Larry Wall 的玩笑式总结,书里认真展开了16:

美德表面意思真正的意思落地动作
懒惰不想干活不遗余力地减少整体劳力——让后来人轻松重复的事交给人做过的工具:先写代码,再做工具,最后自动化
急躁发脾气计算机偷懒时感到愤怒——等不了低效立即修改;并为还没发生的问题预做准备
傲慢目中无人极强的自尊心——交出去的东西不许人挑出毛病按功能模块化管理自己的产出,让维护无愧于人

「懒惰」的执行细节最实用,书里给了三步17:先明确手动作业的流程, 再把能自动化的部分自动化——别一上来就追求自动化,先找出自动化收益最大的那块; 文档也照此办理:不追求整理得漂亮,追求搜得到——把以后查找时可能用的关键词写进内容与文件名, 全文搜索胜过分文件夹翻找18

「急躁」的落地是雏形化:分清哪些部分不会变、哪些会变; 以不变的部分为基础搭雏形,把会变的部分做成可调的参数或配置,「保证能自由修改」—— 用户需求五花八门,与其追着改,不如提供设置文件让用户自定义19。 「傲慢」的落地是模块化:按功能整合的模块,下个项目直接复用20

这一节还有一段必须完整带走的「关联信息」——高强度工作对程序员没有意义21:

长时间泡在办公室会制造贡献的错觉;我们越削减在工作上花费的时间和精力, 对项目的实际贡献就越大。编程是高度脑力劳动,加班的恶劣影响远超所得—— 「拖着疲惫的身躯是写不出好代码的」;疲惫还容易迁怒于人,团队氛围随之恶化。 「在脑力劳动方面,工作时间与劳动成果并不成正比。」

这和第 06 章的技术负债是同一枚硬币:疲劳产出的代码,就是明天要还的债。

5. 无我编程:自我的度

这一节回答:把代码交出去给别人看,心态怎么摆。

无我编程(egoless programming)出自温伯格《程序开发心理学》: 编程时舍弃「自负」与「自尊」,主动把代码给人看、请人批评; 看的人也要放下优越感,「双方都应纯粹为创造出更好的东西而交流」22

它有一条著名的十诫,书里全列了23。挑承重的几句: 理解并接受自己也会犯错;你不同于你的代码——批评代码不是批评你; 不要在没有沟通的情况下重写别人的代码;真正的权威来自知识而不是地位; 「宽待他人,严待代码,批评的对象是代码而不是人」。

作者补了一个重要的平衡:自我的度。自我是干所有事的原动力, 「单方面扼杀自我,牺牲自己为他人和团队服务,是一种 Win-Lose(一方赢,一方输)的行为」—— 把自我和工作完全分离既做不到、也不该做。要控制的是自我的度24。 这与三大美德里的「傲慢」并不矛盾:傲慢对代码,无我对评价。

6. 语境切换:打断的代价

这一节回答:为什么「就问你一句」的破坏力远大于一句话。

第 02 章讲了语境是理解代码的脚手架;这里讲它的反的一面——切换语境的代价。 书里的机制25:程序员脑中装着当前问题的工作集(当前任务相关的全部背景与状态); 被打断=卸载当前工作集、加载别的;回来=重新加载。 最贵的代价是流态(flow,沉浸状态)被打破:进入流态需要15 分钟以上的连续专注; 流态中效率大幅提升,而「一个插入事件就能轻松把我们打回原形」。

书里把这笔账算到了工资尺度26:接一通电话平均 5 分钟,重返流态 15 分钟—— 一通电话实际浪费 20 分钟;一天 10 通,半天没了;20 通,一整天没了。 「晚上写代码更有效率」的感觉,源头多半是这里:不是夜里灵感好, 是夜里没人打断。

管理层的误解也点到了:管理者的日常工作就是不断应对插入事件, 所以他们很难共情程序员对流态的需求——书里把这归为需要文化与意识改革的问题: 「在任何条件下,都要让程序员能在连续的时间内进行工作」27。 这条对个人同样成立:把打断集中化(固定时段处理消息)、把深度时段保护起来。

7. 作者的判断与证据

书里给了证据的:

  • 三大美德出自 Larry Wall(Perl 之父),是社区公认的行话,书内给出来源;
  • 无我编程十诫逐条引自温伯格《程序开发心理学》,有明确文献;
  • 泰迪熊服务台是一个具体可查的制度案例。

作者的推测与展开,要分开看的:

  • 15 分钟进入流态、5 分钟接电话是经验估值,书里未给实验出处;方向与专注力研究一致,数值当量级看;
  • 「越削减时间贡献越大」是修辞性反直觉表述,严谨读法是「时间投入与产出非线性」,不是「越少越好」。

8. 边界与局限

  • 曳光弹不适用于「方向本身未定」的项目——骨架立错了,拆骨架比拆原型更贵(原型路线的存在正是为此);书里的分工已经暗示,但没有给出选择判据:概念不确定→原型,概念确定→曳光弹。
  • **TDD 的「一点测试一点代码」**在本节只是节奏描述;完整的 TDD 流程(红-绿-重构)超出本书范围。
  • 无我编程十诫诞生于 1971 年的卡片式协作语境;代码评审工具化的今天,「给人看」的形态变了,原则未变但载体重建,书内无此讨论。
  • 打断问题没有给个人解法:书里把解法押在职场文化改革上;个人层面的缓冲(把沟通改成不打断人的方式、预留专注时段)是后来的实践,本拆解补此一句(补充(不在书里,来自通用知识))。

9. 可带走的

  1. 新项目第一周就该有一条「能跑通的斜线」:从输入到输出,糙没关系,先让它发光;
  2. 分清原型和曳光弹:没想清楚→做原型学,学完就扔;想清楚了→打曳光弹,骨架留存;
  3. 一次一件事,一次一个提交——回滚永远只回滚一步;
  4. 重构不顺手改名,改名不顺手重构:每步一个意图;
  5. 卡住 10 分钟,就讲给橡皮鸭:讲不圆的那段就是病灶;
  6. 懒出工程水平:重复第二次的手工活,开始考虑自动化;文档先保证可搜索;
  7. 急躁用在预想上:把会变的部分做成配置,别等用户来催;
  8. 傲慢留给代码:交出去的东西,维护要无愧于人;
  9. 批评代码,不批评人;也接受「你≠你的代码」;
  10. 保卫流态:一次打断 20 分钟,把沟通集中到固定时段。

10. 原文地图

主题原书章原文位置
曳光弹战术与编程对应6.1 曳光弹text/73-ch06.txt:13(搜「曳光弹」) · text/73-ch06.txt:21(搜「优先检验」)
曳光弹四收益6.1 曳光弹text/73-ch06.txt:35(搜「用户」) · text/73-ch06.txt:55(搜「测试和调试」) · text/73-ch06.txt:67(搜「演示」)
骨架留存6.1 曳光弹text/73-ch06.txt:11(搜「骨骼」) · text/73-ch06.txt:118(搜「框架本身会存留」)
原型与曳光弹的区别6.1 曳光弹text/73-ch06.txt:89(搜「区别」) · text/73-ch06.txt:97(搜「装车」) · text/73-ch06.txt:124(搜「学习」) · text/73-ch06.txt:126(搜「侦查活动」)
原型样品/试制品6.1 曳光弹text/73-ch06.txt:131(搜「样品」)
内部测试/Dogfooding6.4 内部测试text/73-ch06.txt:540(搜「以用户的身份」) · text/73-ch06.txt:546(搜「功能过剩」) · text/73-ch06.txt:551(搜「真正地作为用户」)
一步一步定义与理由5.5 一步一步走text/72-ch05.txt:444(搜「循序渐进」) · text/72-ch05.txt:454(搜「有效率」)
回滚与类 A 类 B 例子5.5 一步一步走text/72-ch05.txt:459(搜「回滚」) · text/72-ch05.txt:461(搜「类 A」)
新旧并存替换5.5 一步一步走text/72-ch05.txt:468(搜「替换」)
重构不顺手改名、TDD5.5 一步一步走text/72-ch05.txt:482(搜「移动函数」) · text/72-ch05.txt:485(搜「测试驱动开发」)
思考一步一步、边写边思考5.5 一步一步走text/72-ch05.txt:441(搜「一步一步」) · text/72-ch05.txt:512(搜「边写边思考」)
橡皮鸭定义与效果6.5 橡皮鸭调试法text/73-ch06.txt:569(搜「橡皮鸭」) · text/73-ch06.txt:575(搜「立竿见影」)
机制:假定条件与无法说明6.5 橡皮鸭调试法text/73-ch06.txt:584(搜「假定条件」) · text/73-ch06.txt:587(搜「无法说明」)
泰迪熊服务台、同事的时间6.5 橡皮鸭调试法text/73-ch06.txt:599(搜「泰迪熊」) · text/73-ch06.txt:605(搜「宝贵的」)
三大美德定义5.1 程序员的三大美德text/72-ch05.txt:15(搜「懒惰」) · text/72-ch05.txt:20(搜「急躁」) · text/72-ch05.txt:10(搜「傲慢」)
懒惰的自动化三步、文档可搜索5.1 程序员的三大美德text/72-ch05.txt:71(搜「自动化」) · text/72-ch05.txt:76(搜「文档」) · text/72-ch05.txt:79(搜「关键字」)
雏形化、设置文件5.1 程序员的三大美德text/72-ch05.txt:65(搜「雏形」) · text/72-ch05.txt:90(搜「设置文件」)
高强度工作没有意义5.1 程序员的三大美德text/72-ch05.txt:99(搜「高强度」) · text/72-ch05.txt:105(搜「削减」) · text/72-ch05.txt:119(搜「成正比」)
无我编程与十诫5.4 无我编程text/72-ch05.txt:382(搜「舍弃自我」) · text/72-ch05.txt:401(搜「十诫」) · text/72-ch05.txt:420(搜「批评的对象」)
自我的度5.4 无我编程text/72-ch05.txt:422(搜「自我的度」) · text/72-ch05.txt:428(搜「一方赢」)
语境切换与流态6.6 语境text/73-ch06.txt:816(搜「语境切换」) · text/73-ch06.txt:830(搜「流态」) · text/73-ch06.txt:839(搜「15 分钟」)
一通电话 20 分钟6.6 语境text/73-ch06.txt:849(搜「20 分钟」)
文化改革6.6 语境text/73-ch06.txt:865(搜「意识改革」)

Footnotes

  1. 出处:「6.1 曳光弹」第 13 段(text/73-ch06.txt:13,搜「曳光弹」)与第 29 段(text/73-ch06.txt:29,搜「同一环境」)。

  2. 出处:「6.1 曳光弹」第 21 段(text/73-ch06.txt:21,搜「优先检验」)与第 24 段(text/73-ch06.txt:24,搜「正式代码」)。

  3. 出处:「6.1 曳光弹」第 35 段(text/73-ch06.txt:35,搜「用户」)至第 70 段(text/73-ch06.txt:70,搜「明确」)。

  4. 出处:「与原型的区别」第 96 段(text/73-ch06.txt:96,搜「配送中心」)。

  5. 出处:「与原型的区别」第 126 段(text/73-ch06.txt:126,搜「侦查活动」)。

  6. 出处:「6.4 内部测试」第 540 段(text/73-ch06.txt:540,搜「以用户的身份」)与第 546 段(text/73-ch06.txt:546,搜「功能过剩」)、第 551 段(text/73-ch06.txt:551,搜「真正地作为用户」)。原条目的英文副题就是 Dogfooding / Eating your own dog food(text/73-ch06.txt:526,搜「Dogfooding」)。 2

  7. 出处:「5.5 一步一步走」第 444 段(text/72-ch05.txt:444,搜「循序渐进」)。

  8. 出处:「5.5 一步一步走」第 454 段(text/72-ch05.txt:454,搜「有效率」)、第 459 段(text/72-ch05.txt:459,搜「回滚」)、第 463 段(text/72-ch05.txt:463,搜「检查起来也比较简单」)、第 468 段(text/72-ch05.txt:468,搜「替换」)。

  9. 出处:「5.5 一步一步走」第 482 段(text/72-ch05.txt:482,搜「移动函数」)与第 485 段(text/72-ch05.txt:485,搜「测试驱动开发」)。

  10. 出处:「思考也要一步一步来」第 441 段(text/72-ch05.txt:441,搜「一步一步」)。

  11. 出处:「逻辑思考的秘诀」第 512 段(text/72-ch05.txt:512,搜「边写边思考」)。

  12. 出处:「6.5 橡皮鸭调试法」第 569 段(text/73-ch06.txt:569,搜「橡皮鸭」)与第 575 段(text/73-ch06.txt:575,搜「立竿见影」)。

  13. 出处:「6.5 橡皮鸭调试法」第 584 段(text/73-ch06.txt:584,搜「假定条件」)与第 587 段(text/73-ch06.txt:587,搜「无法说明」)。

  14. 出处:「6.5 橡皮鸭调试法」第 599 段(text/73-ch06.txt:599,搜「泰迪熊」)。

  15. 出处:「6.5 橡皮鸭调试法」第 605 段(text/73-ch06.txt:605,搜「宝贵的」)。

  16. 出处:「5.1 程序员的三大美德」第 15 段(text/72-ch05.txt:15,搜「懒惰」)、第 20 段(text/72-ch05.txt:20,搜「急躁」)、第 10 段(text/72-ch05.txt:10,搜「傲慢」)。

  17. 出处:「5.1 程序员的三大美德」第 71 段(text/72-ch05.txt:71,搜「自动化」)。

  18. 出处:「5.1 程序员的三大美德」第 76 段(text/72-ch05.txt:76,搜「文档」)与第 79 段(text/72-ch05.txt:79,搜「关键字」)。

  19. 出处:「5.1 程序员的三大美德」第 65 段(text/72-ch05.txt:65,搜「雏形」)与第 90 段(text/72-ch05.txt:90,搜「设置文件」)。

  20. 出处:「5.1 程序员的三大美德」第 65 段(text/72-ch05.txt:65,搜「模块化」)。

  21. 出处:「高强度工作对程序员而言没有意义」第 99 段(text/72-ch05.txt:99,搜「高强度」)、第 105 段(text/72-ch05.txt:105,搜「削减」)、第 110 段(text/72-ch05.txt:110,搜「脑力劳动」)、第 119 段(text/72-ch05.txt:119,搜「成正比」)。

  22. 出处:「5.4 无我编程」第 382 段(text/72-ch05.txt:382,搜「舍弃自我」)。

  23. 出处:「5.4 无我编程」第 401 段(text/72-ch05.txt:401,搜「十诫」)至第 420 段(text/72-ch05.txt:420,搜「批评的对象」)。

  24. 出处:「自我的度」第 422 段(text/72-ch05.txt:422,搜「自我的度」)与第 428 段(text/72-ch05.txt:428,搜「一方赢」)。

  25. 出处:「程序员的语境切换」第 816 段(text/73-ch06.txt:816,搜「语境切换」)与第 830 段(text/73-ch06.txt:830,搜「流态」)。

  26. 出处:「程序员的语境切换」第 849 段(text/73-ch06.txt:849,搜「20 分钟」)。

  27. 出处:「程序员的语境切换」第 865 段(text/73-ch06.txt:865,搜「意识改革」)。