判断一个工程师是否可靠,不能只看他能否解决复杂问题。很多返工来自普通工作:修改了规则却忘记另一处,沿用没有核实的假设,提交之后才发现运行环境不同。这些事情单独看都不大,积累起来却会让交付难以预测。
《程序员修炼之道》值得反复读的部分,正是它对日常工作的关注。David Thomas 和 Andrew Hunt 在 2019 年的二十周年版中,继续讨论知识重复、正交性、可逆性和反馈等问题。出版方介绍与目录
这些词看起来抽象,但可以放回一次普通需求里:怎样减少遗漏,怎样尽早发现理解错误,怎样给尚不确定的决定留下修改空间?
相似代码未必表达相同知识
下面用一个构造的文件上传场景说明。
头像上传和附件上传都限制文件大小,于是开发者把两段检查合并,使用同一个上限。过了一段时间,产品允许更大的附件,头像限制仍然保留。公共方法只好增加类型参数,再增加一个“是否使用默认值”的开关。
最初两段代码形状相同,背后的理由却不同。头像限制服务于展示与处理成本,附件限制来自业务约定。把它们合并,反而让两项可以独立变化的规则互相牵制。
DRY 经常被简化成“不要复制代码”。原著公开节选讨论的重点是知识和意图的重复表达,而非单纯的文本相似。DRY 原著节选
本文把这个区别应用到上传场景:同一种附件的大小限制,若同时散落在选择器、上传服务和错误提示中,才需要认真处理。规则调整时,三个地方必须同步,否则用户可能选得进却传不上,或者收到错误的提示。
处理方法不一定是建一个庞大的公共类。可以让限制来自同一个规则定义,选择器与上传服务分别消费它,提示文字也使用实际值。服务端仍需独立验证,客户端共享定义并不能替代服务器的权限与安全检查。
是否合并之前,先问两段逻辑为什么会改变。如果同一项业务决定要求它们一起变,就存在重复知识;如果变化原因不同,保留各自表达往往更清楚。
把需要独立变化的部分分开
正交性在工程里可以先按“彼此少受影响”理解。修改上传进度的显示方式,不应该同时要求修改文件存储规则;更换网络库,也不应该重新解释哪些文件允许上传。
这不意味着每个部分完全没有依赖。上传页面仍要知道进度和失败结果,但它可以不必知道底层请求对象的类型。网络实现通过项目自己的结果约定交接,页面就能集中处理用户体验。
一种检查方式是回看最近几次修改:哪些文件总是一起改,它们是否共同表达一项规则?如果展示、存储和传输每次都同时变化,需要查明原因。有时是新功能确实横跨整条链路,有时只是页面持有了过多底层细节。
不要为了减少一起修改的文件数量,把依赖藏到全局对象或运行时字符串里。那样只是在静态代码中看不见关系,故障时反而更难追踪。边界是否有效,应看责任和行为能否说明白,而不是依赖图是否画得好看。
不确定的决定,先考虑怎样退出
开发早期,文件存储方案、第三方库或接口形态常常还没有足够证据。可逆性给出的工作方向是:承认决定可能改变,并尽量控制改变时需要付出的成本。
上传示例如果尚未确定存储服务,可以先用业务里的文件标识贯穿流程,在适配处处理供应商地址。这样更换服务时,页面不必跟着理解新的 URL 规则。但它只是降低代码迁移成本,不能让已经上传的数据自动搬家。
数据、公开协议和用户操作习惯,比内部类名更难替换。选择存储方案时,还需要弄清导出能力、旧链接有效期和迁移过程。写一个接口不能把这些现实约束抹掉。
也无需为所有决定都预留多种实现。如果只有一个确定的简单需求,提前建立插件体系可能比未来修改更贵。值得隔离的通常是眼前已存在的不确定项,或替换成本明显较高的依赖。
做决定时可以留下几行说明:采用什么方案,依据是什么,哪个假设还没证实,出现什么情况需要重新考虑。维护者以后就能根据条件复查,而不用把当年的临时选择当成永久规范。
先走通一条路径,再铺开功能
开发者有时会先写完选择器,再写上传服务,最后接页面。每层局部测试都通过,联调才发现服务器要求的标识与本地模型对不上。
书中的曳光弹思路,可以用来理解另一种顺序:先做一条很薄、能够贯穿系统的真实路径,再逐步扩展。对上传功能,这条路径可以只支持一种文件,却包含选择、发送、服务器响应和页面结果。
原型也有价值,但学习目标不同。为了比较两种进度展示,可以用假数据快速做界面原型;它没有证明网络流程成立。贯穿路径则需要真正连接各部分,并按准备保留的代码质量实现,不能把临时模拟误当作完成。
这一节的场景是本文的应用判断。它的收益不是保证更快写完所有代码,而是把接口理解、身份传递和错误约定的问题提前暴露。越晚发现共同假设错误,返工涉及的范围通常越大。
反馈要能支持下一步判断
“已经验证”是一句过于宽泛的话。上传成功一次,说明那组输入和环境能走通;它没有说明取消可靠,也没有说明弱网重试不会重复提交。
可以根据本次变化选取反馈:文件大小规则用边界样例验证,取消行为用明确的事件顺序检查,上传结果用一次完整流程确认。每项验证都应回答一个具体问题,而不是运行大量命令后获得安心感。
报告结果时,也应说明证据的范围。模拟服务能够稳定复现超时,但不能代替真实服务的兼容验证;模拟器里完成上传,不等于已经检查所有设备环境。对尚未验证的部分明确留下记录,接手的人才知道接下来需要做什么。
反馈速度并非只有测试运行时间。一个失败结果若无法定位原因,即使几秒出现也很难使用。输入、运行方式和关键结果能够重现,才能把“好像有问题”变成可以处理的工作。
把原则变成一次小的改进
这些方法不必一次全部引入。可以从最近一次返工开始:是规则遗漏、边界牵连,还是直到联调才发现假设错误?然后选择一项对应的调整。
例如把附件大小限制集中表达,保持头像规则独立;为一个不确定依赖记录替换条件;让下一项功能先通过完整链路,再扩展类型。完成后再观察它是否减少了同类困难,而不是只检查是否符合某个原则名称。
每次交付前,可以留下四个简短答案:
- 本次改变的业务规则,是否还有其他表达需要同步?
- 哪项假设仍未验证,它影响什么结果?
- 如果当前方案不合适,替换会触及代码、数据还是外部承诺?
- 有哪些可重现的证据,支持这次修改已经完成?
一个可靠的工作方式,允许判断出错,但会让错误尽早显露,也让后来者能够继续处理。它不依赖每次都有人记住全部细节,而是把容易遗漏的地方变成明确的规则、边界和反馈。