读书分享:《程序员修炼之道:通向务实的最高境界》的思想经验 - 阿里技术

相较于全书众多的干货笔记,这篇文章是个别思想经验的总结,希望和大家交流。

武侠影视剧中,江湖各路豪杰可以多年苦苦追寻一本武林秘籍,希望能够得到高人指点,从而功力突飞猛进。对于程序员来说,《程序员修炼之道》就是顶尖高手的智慧结晶,它的第一版风靡了二十年,更难能可贵的是,二十年后原作者又与时俱进重写了第二版。我们把这本书认真读了一下,相较于全书众多的干货,这篇文章只是个别思想经验的总结,希望和大家交流,如果能激发大家对原书的兴趣,自然更好。

ETC

能适应使用者的就是好的设计。对代码而言,就是要顺应变化。因此要信奉ETC原则(Easier To Change,更容易变更)——就该如此。

据我们所知,无论是什么设计原则,都是ETC的一个特例。

为什么解耦很好?因为通过隔离关注焦点,可让每一部分都容易变更——此谓ETC。

为什么单一职责原则很有用?因为一个需求变化仅体现为某个单一模块上的一个对应变化——此谓ETC。

为什么命名很重要?因为好的命名可以使代码更容易阅读,而你需要通过阅读来变更代码——此谓ETC!

我们常讲要对线上变更有敬畏之心,ETC原则是一个很重要的意识,因此我们在工作中要思考怎么样设计代码才能更方便、更高效、更不容易出错地变更。当我们针对一个任务有多个实现方案时,可以考虑把是否符合ETC原则加入到选择指标中。

比如说代码依赖某些值,这些值后面可能会变动(可能是因为业务逻辑变了,或政府出了个新政策或用户的需求变了等等),那可以把这些值放在外部,作为配置项。

DRY不仅限于编码

我们认为,想可靠地开发软件,或让开发项目更容易理解和维护,唯一的方法是遵循下面这条被称为 DRY 的原则:

在一个系统中,每一处知识都必须单一、明确、权威地表达。

DRY原则大家也很熟悉,但是如果认为DRY只是说不要复制粘贴源码,那还远远不够。DRY指的是你对知识和意图的复制,哪怕用两种不同的形式在不同的地方,它们表达的东西可能是一回事。

当我们要改变一个业务逻辑时,是否在不同的地方以不同的形式进行了变更?包括代码、文档、数据库Schema、数据结构、注释等等。如果我们的这个逻辑(或知识)在那么多地方重复表达,那么可以想到的是,用不了多久这些地方的信息就会不同步;如果你想确保它们同步,那每次变更就要花更多的时间和精力。

维护一个项目概念列表

每个项目都有自己的词汇表:对团队有特殊意义的术语。“Order”对于开发在线商店的团队来说是一回事,而对于记录宗教团体的世系的应用程序来说,意味着完全不同的另一件事。重要的是,团队中的每个人都知道这些词的意思,并始终如一地使用它们。

一种方法是鼓励大量的交流。如果每个人都参与结对编程,并且频繁地交换结对,那么术语就会渗透性地传播开来。

另一种方法是使用项目术语表,列出对团队有特殊意义的术语。这是一个非正式的文档,可以在 wiki 上创建并维护,也可以将索引卡片挂在墙上。

过一段时间,项目术语将会有自己的生命。随着每个人都熟悉了这些词汇,就能够把这些术语用作简称,准确而简洁地表达许多意思。(这正是模式语言所指。)

维护一个概念列表,可以保证团队交流的准确性,避免你说的是A,我理解成B的情况。

一个项目中有不同的角色,业务方、产品、前端、后端、算法、数据,对于一起开会时经常提起的概念,大家的理解并不一定一致。举个简单的例子,比如业务方有个二分类的需求,要从一些商户中找到其中不合规的商户,模型预测结果如下图所示,但是一个人以为的准确率是(a + d) / (a + b + c + d),另一个人以为的是d / (b + d)。如果没有在项目之初明确下来大家关注指标的具体含义,那可能出现各说各的的情形。技术按照一个计算逻辑承诺并达成了准确率指标,业务方按照另一个逻辑计算后发现准确率远远没达到,这会给团队合作带来障碍。

维护一个项目概念列表还有一个好处,就是在制定列表的同时会推敲这个概念的说法是否合适。

举一个场景,政府规定购买某些药必须有48小时内阴性的核酸检测结果,要用模型识别订单是否需要拦截。有人把”识别通过“说成”识别成功“,把”识别拦截“说成是”识别失败“,这样就很容易把失败率和模型的识别错误率当作是一回事(因为”失败“和”错误“这两个词太相似了)。其实这是两个完全无关的指标,如果短时间内有10个订单全部拦截了,而且用户确实不符合防疫规定,真实的识别错误率是0,但是用失败称呼拦截很容易以为模型识别全错了。

概念是“思维的细胞”,维护一个概念列表不仅有利于项目成员内部的合作,也有利于给上级或其他人汇报时听众能更容易准确理解你的意思。因为项目成员有更多的背景知识,用词不准确不一定会引起歧义,大家都能理解,但是非项目成员不一定都能准确理解。

帮助业务方理解他想要什么

新手开发人员经常犯的错误是,把这种对需求的声明照单全收,然后实现对应方案。

根据我们的经验,最初对需求的声明,往往并非绝对化的要求。客户可能没有意识到这一点,但一定希望你能一起去探索。

下面给出一个简单的例子。你在一家出版纸质书和电子书的公司工作。你接到一个新的需求:所有 50 美元以上的订单都应该免运费。停一秒钟,把自己带入这个场景。首先想到的是什么?你有大把机会发现问题:

1、50 美元含税吗?

2、50 美元算没算上本应支付的运费?

3、50 美元必须全是用来买纸质书吗?还是允许在同一订单中有部分电子书?

4、包邮指的是怎样的服务?是加急还是平邮?

5、如果是国际订单如何处理?

6、未来会经常改变 50 美元这个限制吗?

这就是我们所做的。当某些事情看起来很简单的时候,我们却会去寻找那些边缘情况,并就其不胜其烦地问人。

很可能客户已经想到了其中的一些问题,并假定实现将以某种方式工作。问这类问题只是把信息明确下来。但有些问题可能客户之前并没有考虑到。这就是事情变得有趣之处,也能让好的开发人员从此处事老练。

上面的例子是通过帮助业务方理解他的需求,从而让问题变复杂了,那是因为问题本身就复杂,业务方最初没想清楚。你如果按照最简单的假定去做,可能会出现资损、客诉等各种问题。

有些场景下,帮助业务方理解他想要什么会让问题变得简单。

比如在某些合规审核场景下,业务方需要你从某种证照图片中提取法人姓名,你在OCR识别之后准备训练一个NER模型用来识别姓名,需要的注意点很多,比如这是全国的商户,少数民族的姓名通常的NER模型识别不出来或错误率高(针对性训练的话又需要标注数据了)。但仔细想一下,业务方想要的真的是这个姓名吗?询问之后发现,业务方想要的是图片中的姓名是否和已知的姓名一致。问题突然变简单了!你只需要判断真实姓名是否存在于OCR识别的字符串中即可。通过准确理解业务方的目的,你更快更好地完成了任务。

防御性编程

每个人都觉得,地球上只有自己一个好司机。全世界都在闯红灯、实线变道、不打灯就转弯、开车发短信,总之都不符合我们的标准。所以我们需要防御性驾驶。在麻烦发生之前就做好准备,预料到意料之外的事情,永远不要把自己置于无法自拔的境地。

与编程类比,上述理论也明显成立。我们不断地与他人的代码交互,代码可能不符合我们的高标准,或需要处理可能有效也可能无效的输入。所以我们被教导要防御式编程——有任何疑问,都要去验证我们得到的一切信息;使用断言来检测错误数据,不信任任何可能来自潜在攻击者或有恶意者的数据;做一致性检查,对数据库的列设置约束——做完这些,我们通常就会自我感觉良好。

务实的程序员则更进一步,他们连自己也不相信。既然没人能写出完美的代码,那么也包括自己在内。务实的程序员会为自己的错误建立防御机制。我们将在契约式设计中描述第一个防御措施:客户和供应商必须就权利和责任达成共识。

当我们和他人的代码交互,比如调用他人的接口时,最好要考虑到正常情况之外的一些corner case,它们可能非常罕见,但是最好不要在出现时导致故障的发生。

继承税

我刚开始工作时,看到团队的一大堆代码,真的是头皮发麻,遇到过代码上一次变更是10年前(非阿里),结果团队待得最久的成员才待了7年,也没啥文档,没人知道咋弄。只能硬看。这就是文档的重要性。

记得有一次,我发现某个地方有问题,理解上应该是如果errorMsg非空,那就打印errorMsg,结果代码是如果errorMsg为空,那就打印errorMsg。但是问了团队同事,他说,也不一定,都不知道写代码的人想干嘛,建议别动。我本来想改掉,听他这么说,心想还是算了,和我这个需求无关,到时候改出问题了咋办。做一些我认为正确但是有风险,做好了也收益不大的事情,还是需要勇气的。而当时在试用期的我,并没有这份勇气,这真是个非常现实的问题。屎山也许就是这么来的。

本书提了一些方案,比如少用继承,多用interface,使用配置等等。同时也提到说,不要因为懒,就放弃做决策,把分歧都做成配置。也不要做的太过,把所有东西都做成配置,会导致修改和维护起来非常难和低效。

学会沟通

有些程序员喜欢一整天都待在电脑前,不说一句话。但线上或线下的开会、沟通、分享等也是不可或缺的。这本书强调了沟通的重要性。一个是做的事情本身,一个是传达和表达出来,两个都很重要。如果心里有抗拒,那就把中文/英文当作另一门编程语言,去掌握和用好它。

小实验

读这本书的时候,我们组一位同学做了一个很有意思的实验,组会时大家也一起进行了讨论,这里分享给大家。请看下面这段话:

“不要把问题归咎于别人或其他什么事情上,也不要寻找借口。不要把所有问题都归咎于供应商、编程语言、管理或是同事。这些因素都可能是问题的一部分。它们的确会对解决方案造成影响,但不是给你的借口。”

假设是你的主管对你说如上这番话,你觉得这是在pua你吗,还是确实讲的很有道理不是pua你?大家心里也许有一个答案了。

把这个问题发到脉脉上,让大家投票,近500人参与投票,统计下来有60%的人认为这是PUA。 

我们可以把这个问题再扩展一下, 如果你老板和你在平时轻松的状态下和你讲这番话,和打绩效的时候讲这番话,同样的话你的感受是否会不一样?如果是不在同公司的好友讲的呢?如果是你敬重的前辈呢?

而如果,这番话来自本书《The Pragmatic Programmer 程序员修炼之道》呢?

说明 :

  1. 本身不是话术的问题,而是谁说出来的问题,场景问题;

  2. 你对他是否“信任”很重要, 如果信任就不觉得是PUA,不信任就觉得是PUA;

  3. 是否有利益关系,比如外部大佬跟你没有利益关系。

如果要总结的话,就是我们心里知道说话人的动机到底是不是真的为我好,在这个前提下,对同一番话的解读也会不同。

其实这些思考和讨论都不局限于这本书本身了,感觉包含了对于生活和人生的思考。这也许是组队读书的好处之一。

最后,这本书只读一遍是不够的,需要反复阅读并在实践中增进理解。

1