跳转到主要内容

第 12 章 · 干将莫邪

工具应该由项目统一提供而不是各人自备:目标机器、仿真与编译器平台、受控的程序库、文本编辑系统,以及高级语言与交互式编程。

题记是一句谚语:“巧匠因为他的工具而出名。”

这一章讲了什么

作者先批评一种他称为"经营五金店"的做法:每个骨干人员都仔细保管自己工作生涯中搜集的一套工具集,把它们当作个人技能的直观证明——编辑器、排序、内存转储、磁盘空间实用程序各有一套。他说这对软件项目来说是愚蠢的,理由三条:项目的关键问题是沟通,个性化工具妨碍而非促进沟通;机器与工作语言一变,工具的生命周期就结束;而开发和维护公共通用工具的效率无疑更高。

但仅有通用工具不够——专业需要和个人偏好同样需要专用工具。所以他在第 3 章的外科手术队伍里为每个团队配了工具管理人员:管理通用工具、指导客户和老板怎么用,同时编制老板需要的专用工具。他还特别提醒一个反直觉的组织结论:把所有分散的工具管理人员集中成一个公共工具小组,效率反而不会更高——专用工具必须贴着具体团队的需求长出来。

需要项目经理计划组织的工具是一张清单:计算机设施(含硬件与使用安排的策略)、操作系统、语言及明确的使用方针、实用程序、调试辅助程序、测试用例生成工具,以及处理文档的字处理系统。接下来按机器角色分述。

目标机器是软件所服务的对象,程序必须在这一类机器上完成最后测试;辅助机器则在开发过程中提供服务。为原有机型开发新操作系统时,同一台机器会兼具两种角色。他对目标机器提的要求很具体:速度不必快,但要有若干兆字节主存、百兆字节在线硬盘和终端;字符终端够用,但必须比每秒 15 字符的打字机快;大容量内存能支持功能测试后的覆盖与裁剪,极大提高生产率。此外必须有调试机器或软件,让各类程序参数被自动计数与测量——他把内存使用模式称为"非常强大的诊断措施",能查出不合逻辑的行为或性能意外下降的原因。

机器时间的进度安排是这一节最生动的部分。OS/360 开发早期,机器时间极度稀缺:他们按经验提前预订了 System/360 的使用小时数,结果起初机器日复一日空着,突然有一天十六个系统全部上线,资源配给立刻失控——因为"每个人在同一时间开始调试自己的第一个组件"。他们先尝试集中机器与磁带库、组建专业操作团队、把调试任务批处理化,每天跑四次、周转 2.5 小时(实际要求 4 小时),甚至动用一台带终端的 1401 做调度与跟踪。但队伍仍然过度运转,经历几个月"缓慢周转、相互指责和极度痛苦"之后,他们改成把机器时间切成连续的块:例如整个十五人的排序小组拿到四到六小时的使用块,自行决定怎么用,即使机器空着别人也不能占用。

结果是:机器利用率可能略降,但生产率提高了。理由是"持续的精力集中能减少思考时间"——六小时里连续十次操作,比分散在间隔三小时里的十次操作产出高得多;每次冲刺之后,小组通常还要一两天做文档工作;而三人左右的小组最能有效共享时间块。作者还顺带记下一个跨越三代机器的现象:系统调试总是夜班性质的工作,“技术完全改变了,操作系统出现了,大家喜好的工作方式并没有改变”——因为它生产率最高。

辅助机器和数据服务一节列了六类基础设施:

  • 仿真装置:目标机器是新产品时,逻辑仿真装置能在真机出现之前提供调试平台,而且真机出现之后依然有用。作者强调一句关键话:可靠并不等于精确。仿真在某些细节上肯定无法与真机完全一致,但它的实现至少在一段时间里是稳定的,而新硬件不会。硬件故障往往是间歇性的,不确定性最糟糕,因为它剥夺了开发人员查找 bug 的动力——也许 bug 根本就不存在
  • 编译器和汇编平台:编译软件应运行在可靠的辅助平台上,为目标机器产出目标代码,再立刻转到仿真装置上调试;用高级语言时,大量调试工作可以在真机全面测试之前完成。
  • 程序库和管理:OS/360 中做得最成功的一项,由 W. R. Crowley 领导,连接两台 7010 与一个大磁盘数据库。库按访问规则分成几层:开发库(playpen)里每位开发人员自由处置自己的程序,因为他是拥有者;准备集成时把拷贝交给集成经理放进系统集成子库,此后原作者未经批准不得再改;某个版本被广泛使用后提升为当前版本子库,原则上不可更改,用于所有新模块的集成与测试。程序目录跟踪每个模块每个版本的状态、用途与变更。作者从中提炼出两个理念——受控(拷贝由经理负责,变更需他独立授权)与让发布的进展正式化,并把开发库与集成、发布正式分离——并称这是"OS/360 工作中最优秀的成果之一",因为它其实是管理技术,贝尔实验室、ICL、剑桥大学都独立发展出了类似做法,而且同样适用于文档。
  • 编程工具实用程序:内存转储、源文件编辑、快照跟踪、磁带拷贝、打印与目录管理等等——只要一开始就任命工具操作与维护人员,这些工作可以一次做好并随时待命。
  • 文档系统:最能节省劳动力的工具,是运行在可靠辅助平台上的计算机化文本编辑系统(他点名 J. W. Franklin 设计的那套)。没有它,OS/360 手册的进度会远远落后,而且更晦涩难懂。面对"六英尺厚的手册没用"的批评,他的回应是两条:文档规模虽大,但阅读计划是被仔细安排的,应把它当图书馆或百科全书,而不是一系列强制阅读的文章;同时他承认手册有不少地方需要改进,篇幅本可以大幅压缩。
  • 性能仿真装置:最好有一个,并且与逻辑仿真、产品本身使用同一套自上而下的设计方法;要尽可能早开始,并"仔细地听取它们表达的意见"。

最后一节讲当时最重要却未被广泛采用的两种工具:高级语言交互式编程。作者的语气很硬——“我确信只有懒散和惰性会妨碍它们的广泛应用,技术上的困难不再成为借口”。

高级语言的价值在于生产率与调试速度:bug 更少且更容易查,因为它避免了在错误面前暴露所有级别的工作(不只是语法错误,还有寄存器使用不当这类语义问题),而编译器的诊断机制能帮忙定位。至于当年流行的三条反对意见——做不了我想做的事、目标代码太大、跑得太慢——他逐条回应:功能上的限制已经消失,只需要花时间找出做法;新的优化编译器在空间上已令人满意并会继续改进;速度上,优化编译器生成的代码比绝大多数程序员手写的效率更高,真遇到瓶颈,把其中 1%–5% 换成手写代码即可。至于选哪种语言,他认为当时唯一合理的选择是 PL/I。

交互式编程这一节里,他给了一份珍贵的量化证据(来自贝尔实验室 John Harr 的论文):

程序规模(指令)方式生产率(指令/人年)
ESS 代码800000批处理500–1000
7094 ESS 支持120000批处理2100–3400
360 ESS 支持32000交互式8000
360 ESS 支持8300批处理4000

结论是:系统软件开发中,交互式编程的生产率至少是原来的两倍。理由他也讲清了——调试是系统编程中最慢、最困难的部分,而"漫长的调试周转时间是调试的祸根"。最后他补了一条耦合关系:由于电传打字机和打印机终端无法胜任内存转储式调试,交互式工具的有效使用需要高级语言配合——有了高级语言,修改代码和选择性打印结果才变得容易。这两样东西是一对。

我的判断

  • 这一章表面在列工具清单,核心其实是一条组织主张:工具是项目的公共资产,不是个人的技能证明。作者给的三条理由里,第一条最容易被忽略——个性化工具妨碍沟通。工具不统一,本质上就是每个人的输出格式不同,交流成本随即上升。
  • 他对工具管理人员"不能集中成公共小组"的判断,与今天很多组织的直觉相反。我认同他的逻辑:专用工具贴着具体团队的需求,脱离上下文的集中供给会失效。这和站点里"约定要写在项目内的 AGENTS.md 而不是某个中心文档库"是同一件事。
  • “可靠并不等于精确"这句是我这一章最想带走的一句。它把仿真与真实环境的取舍讲透了:在开发阶段,稳定性比保真度更重要,因为不确定性会直接杀死排查 bug 的动机。这条今天仍然适用于容器、模拟器、桩件测试。
  • 程序库那三层(开发库 → 集成子库 → 当前版本子库)以及"受控"的理念,我认为是本章最实用的部分,而且它没有过时:今天的分支策略、代码所有权、发布冻结,本质都是同一套东西。它比"用什么版本控制工具"重要得多。
  • 我对"优化编译器生成的代码比绝大多数程序员手写更高效"这条持保留:它在 1975 年的系统编程语境下成立,但今天手写汇编在热点路径上仍能显著胜出(尤其是嵌入式)。不过我承认他的补充很务实——先全用高级语言,再把 1%~5% 换成手写,这个顺序是对的。
  • 交互式编程那组数据(2 倍)放到今天看反而更极端:现代开发几乎全在交互式环境里,“批处理"这个名字已经消失。作者在五十年前就把它列为"最重要却未被广泛采用"的工具,这个判断后来被完整验证了。

和我手上的工作有什么关系

  • 我的"工具集"目前散在两处:项目内的 tools/(Hugo、Go)与项目外的系统包(pdftoppm、OCR 引擎)。按这一章的标准,这已经算公共工具而非个人工具,但没有统一的说明。我打算在 AGENTS.md 里补一张工具清单,写清每件工具的用途与安装方式——这正是作者说的"工具管理人员"在单人项目里的最小形态。
  • 时间块那条对我有直接价值。我经常把多件事切碎并行处理,结果每件都处在"间隔三小时"的状态。作者的实测结论是连续集中的六小时远胜分散的六小时,因为省下的思考时间属于切换成本。我准备把"给一件事分配连续的时间块"当作排期规则。
  • “受控"那三层库让我重新看待自己的工作流:我的 content/ 相当于开发库(自由改),main 分支相当于集成子库(推送即对外),而发布出去的版本则是当前版本子库(不可更改)。差距在于我缺少集成经理这个角色——目前是我自己既当作者又当集成者,所以作者说的"原作者未经批准不得改动"在我这里形同虚设。至少要给发布加上"发布后改动必须记入会话记录"的约束。
  • 高级语言那条结论,与我在嵌入式上的现实有些出入:那里 C 与汇编仍是主流,编译器优化水平参差。但它的方法论仍可借用——先把热点交给工具,再用实测数据决定哪 1%~5% 值得手写,而不是凭直觉一开始就手写优化。
  • “可靠不等于精确"这条我打算用在测试环境上:与其追求与真机完全一致的模拟平台,不如先保证它稳定可复现;一个稳定但不完美的平台,比一个精确但飘忽的平台更能提高排查效率。

一句话记住

工具是项目的公共资产而不是个人技能证明:统一提供通用工具、贴着团队需求保留专用工具,并优先保证调试平台的稳定——因为不确定性会直接杀掉排查问题的动力。