《程序员修炼之道》| 王木头解读
关于作者
这本书的两位作者分别是大卫·托马斯和安德鲁·亨特,他们不只是非常资深的程序员,还是《敏捷软件开发宣言》17位创始人中的2位。他们为敏捷软件开发建立起了价值观和基本原则。
关于本书
《程序员修炼之道》被一代代开发者奉为圭臬。时隔20年的新版,经过全面的重新选材、组织和编写,覆盖哲学、方法、工具、设计、解耦、并发、重构、需求、团队等务实话题的最佳实践及重大陷阱,以及易于改造、复用的架构技术。本书极具洞察力与趣味性,适合从初学者到架构师的各阶层读者潜心研读或增广见闻。
核心内容
需求不清,是程序员面临的真正挑战,高手程序员重新定义了交付软件的价值到底是什么。每次开发出来的软件交付给用户,它的价值不再只是把实现了某个功能的软件产品交付给用户,它更是探寻用户真实需求的催化剂。催化剂是什么,它虽然不是我们需要的结果,但它却是促成结果的关键要素。
你好,欢迎每天听本书,我是王木头。今天为你解读的这本书是《程序员修炼之道》。
你别被这个名字吓到了,程序员的修炼之道,名字听起来很玄,其实非常实用。这本书就是一个清单手册,有100条,每条都是一个实用原则。如果你去读的话,可能都不相信这是程序员写的书,字里行间透着幽默感。就比如其中一节的标题是“我的源码被猫吃了”,这不是告诉你程序员别养猫,猫会咬坏你的硬盘。这其实是说,如果某天你不小心把代码删掉了,别着急找借口,甩锅给自己的猫,而是要积极地寻找替代方案。这样的幽默感在书里面到处都是。
2020年4月这本书迭代了第二版,更新了很多内容。它的第一版是在20年前出版的,在这20年里面,全世界有很多程序员,都把这本书当作自己的进阶手册。这本书并不是一本程序员入门书,而是让一个程序员从菜鸟进阶成高手的书。
说到程序员,很多人可能会觉得程序员就是一群手艺人,不爱交流,就是闷头做自己的事。虽然看不懂他们在干吗,但是总能看到他们在学习,提高自己的手艺。
不只是普通读者,就连很多刚入行的程序员也都是这么认为的。他们认为程序员就是一个手艺人,只需要实打实地精进自己的手艺,让自己的程序运行得更快更好,更少出现bug,就一定可以成为程序员高手。他们认为程序员本质上和其他手艺人没有区别,就像是日本的寿司之神小野二郎一样,年复一年地精进自己的寿司手艺,最后就能成为顶级大师。
其实,程序员和手艺人还不一样,手艺人追求的是完美,他们会把经过自己手的创造物,不论是一个寿司,还是一把宝剑,或是一个机械零件,都要尽可能地做到完美。而程序员,更准确地说应该是软件工程师、“码农”,他们不像手艺人那样有强烈的理想主义,总是追求完美,相反地,他们是一群务实的高手。
通过后面的讲解你就会明白,在务实这件事上,软件工程师不只是有精湛的技艺,还有深刻的哲学思考。 这也是这本书的中文版副标题,为什么会叫“通向务实的最高境界”。
我这里说有哲学思考不是夸张啊。这本书的两位作者,分别是大卫·托马斯和安德鲁·亨特,他们在软件界可是非常有名的。他们是《敏捷软件开发宣言》17个创始人中的2位。什么是敏捷软件开发你先不用在意,你听到“宣言”先想到的是什么呢?《美国独立宣言》或是《世界人权宣言》,每一个宣言的背后都有着深刻的哲学思考。
《敏捷软件开发宣言》也经常被称为是《敏捷宣言》,它就是程序员务实哲学的集中体现,可以说是现在程序员高手的必修课了。你也别觉得《敏捷宣言》是一小撮程序员的自嗨,其实这个宣言里面的敏捷思想早就已经破圈了。
如果你在公司里面每天都开十几分钟的站立会议,或者总是从领导的嘴里听到要快速迭代,还或者一个产品还在创意阶段,负责人总是要求先把最小可行性产品做出来,这就说明你和你的团队已经被程序员的务实哲学深深地影响了。
如果用一句话来概括程序员心中的务实,那Facebook,也就是脸书贴在墙上的一句话最能代表程序员务实的价值观,那就是“完成大于完美”。
这本书里面,作者们也一直在强调这件事,不论是告诉读者们应该交付“够好即可的软件”,还是嘱咐读者说“你无法写出完美的软件”,其实都是在提醒程序员,放弃对完美的执念。对完美的执念,可能就是阻碍一个程序员从菜鸟走向高手的最大障碍。
为什么说对完美执着反而会成为程序员成长的障碍呢?接下来我就为你详细解读。
我相信,当你第一次听到“完成大于完美”的时候,一定会很惊讶,放弃对完美的追求,这好像不是什么好事吧。如果是一个普通的互联网产品,软件不做到完美,可能就是用起来不方便,但是如果是银行的管理系统,或是航空调度系统,这要是不完美,那就会出大问题。这是对客户不负责啊。
不只是对客户不负责,也是对自己不负责。乔布斯不就要求自己的工程师,就算是客户看不到的内部电路板也必须设计得像艺术品一样吗。虽然这份执着,对客户、对功能没有真正的价值,客户可能根本不知道,但是自己知道啊,这么做是对自己的负责。
甚至你可能会想,“难道高手程序员要放弃追求完美,变成一个世俗的、没有原则的人吗?那样的话,我宁愿不做高手。”
你想错了,高手程序员放弃对完美的追求,不是不负责,反而是最负责的做法,不只是对自己负责,更是对客户负责。
我举个例子,17世纪的著名荷兰画家伦勃朗,有一副经典的画《夜巡》。这幅画可以说是艺术史上的精品,不过它却是一件非常失败的产品。
《夜巡》这幅画,本来是伦勃朗受到了阿姆斯特丹民兵队的委托,给民兵队成员们画肖像。伦勃朗也的确用自己的高超画技完成了这幅画,但是他的委托人们却非常不满意。因为支付了同样报酬的人,有的人在画里就非常不明显,甚至还被人挡住了脸。所以说,伦勃朗追求了画作本身的完美,却完全没有考虑客户的需求,这其实就是没有对客户负责。
然后客户就把伦勃朗告上了法庭,让他的声誉大跌。这也成了伦勃朗人生的转折点,后来他一步一步地走向了破产的边缘,临死的时候贫困潦倒,连一个像样的墓地都没有。这也可以说,伦勃朗为了追求完美没有做到对自己负责。
程序员之所以用不追求完美的方式对客户负责,就是因为在这件事上吃过太多的亏了。我自己就是软件工程专业毕业的,我记得非常清楚,当时学习的所有软件工程的教材里面,都会介绍一个软件项目的流程模型,瀑布模型。瀑布模型就是说,软件开发的过程,从需求分析开始,设计、开发、测试、部署每个过程都必须按照步骤一个结束了再进行下一个步骤,就像是瀑布一样,上游的工作完成了才能流到下游。
这个模型可以说是最有名的模型,但是也是最不实用的模型,20世纪80年代之后,这个模型就几乎不再被使用了。相反,后来所有的软件开发模型,都会把瀑布模型当作一个靶子,可以说都是针对它的问题进行的改进。
其实现在的软件开发,瀑布模型里面说的几个步骤也都需要,就比如需求分析,要想开发软件,那得先知道软件给谁用,实现什么功能,有什么性能要求等等。知道了这些,后面的设计开发才不会做无用功。万一需求没有搞明白,等设计开发完了才发现错了,到头还要重来,浪费时间精力。
所以,按照这个开发模型去做的话,程序员要想对客户负责,对自己负责,那么就必须把每个步骤都做到完美。需求完全搞清楚了,设计才不会出错,设计完美了,开发才可以有效率。如果任何一个步骤有一个小问题,那么在后续的过程中这个问题就会被放大,导致软件无法满足需求,甚至返工重做。
可是,没多久,程序员们就开始因为这个模型吃亏了。因为软件开发要做的事情发生了变化。在计算机刚开始商用的时候,软件开发要实现的产品,一般都是仓库管理系统、财务管理系统等等这样的商业项目。而且,当时主要做的也是把原来需要人在纸上做的事情搬到计算机上。所以,这个时候,程序员要是实现什么功能是很明确的,仓库管理、财务管理,业务流程等等都已经非常成熟了,程序员要做的就是把它们在软件上重做一遍。
但是,再往后就不是这样了,计算机不再是只满足商业需求,也就是2B业务,开始进入个人和家庭,也就是2C业务。尤其是当互联网发展起来之后,很多程序员要开发的软件需要满足的是个人需求。这里多说一下,软件的交付对象,在2B业务时一般称为客户,2C业务一般称为用户,后面更多的是在讲2C的业务软件,所以我就使用用户这个词了。
个人市场上的需求发展变化很快,按照原来的方法开发,需求分析、设计、开发等等一套流程下来,一两年过去了,个人市场的需求早就变了,做得再完美也已经过时了。更要命的是,软件用户自己都说不清楚自己需要什么。你可以想想,微信做出来之前,你会想到自己需要这么一个软件吗?是不是觉得短信和QQ完全就可以满足自己需求了?
需求不清,这才是程序员面临的真正挑战,而他们的“务实”,就是在解决这个问题。怎么做呢?这其实也是《敏捷宣言》里提倡的一个核心,它重新定义了交付软件的价值到底是什么?每次开发出来的软件交付给用户,它的价值不再只是把实现了某个功能的软件产品交付给用户,它更是探寻用户真实需求的催化剂。催化剂是什么,它虽然不是我们需要的结果,但它却是促成结果的关键要素。
如果把软件的价值定义成交付给用户产品和功能,那么的确需要提供完美的产品,因为要对用户负责。但是,如果把软件看作是催化剂,看作是探寻真实需求的工具,那么追求完成而不是完美就会是一个更好的选择。
我想我们都有过这样的经历,和朋友们聚在一起,到了吃饭的时间,想约着一起去吃饭。可是到底去吃什么,大家都说不知道,一直僵持着。只要这个时候,有一个人提出“要不还是吃麦当劳吧”,局面马上就打开了。有人就会接话说,麦当劳距离太远了,还是附近的川菜更好,然后又有人说,最近就是想吃辣,最好还是肉多一点。于是很快就能定下来,去吃了火锅。
这里第一个人提出的麦当劳,就是那个不完美的产品,它的价值不是真的让人去吃麦当劳,而是给了所有人一个靶子,让人以它为基础,提出自己的需求。软件开发也一样,当面对的是一个不确定的市场时,越早喊出“麦当劳”,就可以越早地打破僵局,发现用户的真实需求。不论是不完美的软件,还是“麦当劳”,它们的价值,重点不在用户使用它们,而在于程序员可以通过它们发现真正需求。
很多人都听过,最小可行性产品,也就是MVP,这其实就是用产品当作催化剂的最佳实践之一。开发一个新产品时,不要只相信创始人的个人经验,也不要相信用户调研。有一个想法之后,要尽快把一个功能最精简、开发时间最短、成本最低的产品做出来,从真实的用户那里获得反馈。
当然,关于最小可行性产品的具体内容,可不是我这里说的这么简单,什么样的产品算是最小可行性产品,如何获得真实反馈,又如何从反馈中发掘真实需求,这是有一整套方法论的。这里推荐你去阅读得到电子书里的《精益创业》,我这里就不详细介绍了。
当了解了这些之后,我们回头再看“完成大于完美”这句话,为什么这句话是程序员务实的体现。因为它让程序员别把目标放在结果上,只想着结果要完美,更重要的是过程,别光想着要做什么,还要知道怎样才能做到。高手程序员,不是放弃了完美,而是用更快的速度更少的成本先完成,用真实的产品让用户反馈。
当然了,要想成为一个高手程序员,可不是把思想转变一下,从原来的“追求完美”变成“完成大于完美”就可以了。通过前面的介绍,我想你应该也明白了,说是完成,其实只是实现最终目标的第一步。快速完成最小可行性产品,用它获得了真实反馈,然后呢?肯定还想需要持续地变更和持续地改善。每一次完成,其实都是实现最终目标的一小步。
那么问题来了,怎么才能做到持续变更、持续改善呢?这就是程序员务实的另一方面了。
在这本书里面,作者就强调了一个编写代码的核心原则——ETC原则,也就是Easier To Change的缩写,让变更更容易。可以这么说,所有的编程技巧本质上都是在实现ETC原则。
举个例子,对于程序员来说,普通台式电脑的硬件设计就符合ETC原则。如果内存不够了,可以打开机箱增加一个内存条,如果速度慢了拆下CPU换一个。也就是说,你的需求发生变化后,通过很少的变更就可以继续满足自己的需求,不用把整台电脑都换掉,那样成本会很高。相反地,现在的手机就不符合ETC原则,别说增加内存了,就是电池都不能随便换,如果你需求变了那就只能买新手机。
当然了,这里是用硬件来举例子,硬件更新周期长,不那么容易变更也是没有问题的。但是软件就不一样了,前面讲了程序员可不是做到“完成大于完美”就完了,最重要的是能持续改善。软件的持续改善,那很可能是上午发布的内容下午就有了反馈,就需要进行改善了。如果程序员写的代码不能应对这样的变更,每次都要把代码重新写一遍,那么成本可就太大了。
所以,别看前面说了,要先做一个最小可行性产品,要赶快投放市场获取真实反馈。这的确没错,但是这也只能说对了一半儿,还有另外一半也必须保证才行。那就是这个最小可行性产品的实现,要符合ETC原则,容易变更。
当你听别人说,他有一个绝好的想法,现在正在做最小可行性产品,很快就要上线投入市场了。你可别马上就认为他很了不起,他很务实。你一定还要去问问他,对于变更他还做了什么。如果他什么也没做,只是单纯地追求最小可行性产品,那么最后的成本反而会很高,甚至还不如最直接的做法,把所有功能做全了再上线。
其实ETC不只是实现持续改善的一个简单原则,它本身就是一个实现持续改善的解决方案。如果给程序员最深恶痛绝的事情排个序,需求变更绝对可以排在前三。这其实可以理解,假如说老师给你布置作业,说今天晚上要把《出师表》背下来,第二天检查。结果你晚上都快背完了,收到了老师发来的信息,说作业布置错了,明天不是考《出师表》,而是考《鸿门宴》。知道这个消息,你是不是也得崩溃。
程序员也一样,每当产品经理提出需求后,他们恨不得让产品经理签字画押,承诺一定不改需求了。可是,需求不是产品经理说了算的,真正决定需求的是用户。要想对用户负责,那就要允许需求变更。
在《敏捷宣言》提倡的价值观里面也有一条,响应变化高于遵循计划。其实这也是普通程序员和高手程序员的重要区别,只有先具有了灵活的意识,才能有灵活的解决方案。当一个程序员认为一个需求就应该是板上钉钉的时候,错误的种子就已经被埋下了。
一个高手程序员,面对需求变更,想到的不是让产品经理签字画押保证不变,而是利用ETC原则,让变更更容易,为变更做好准备。
就比如说吧,假如你去学习编程了,那么你一定会学到一个六字真言“高内聚,低耦合”。高内聚的意思就是说设计代码模块的时候,内部功能的聚合程度要尽可能地高,低耦合的意思是说模块和模块之间的耦合程度要尽可能地低。
形象点理解的话,你可以把程序的模块化想象成是给快递打包,要想打包得好,首先必须把快递盒子给塞满了,不能晃荡,这就是高内聚;然后还要把盒子封装好,多缠几圈胶带,不能让里面的东西漏出来,这就叫低耦合。快递漏出来了,最多就是丢东西,代码暴露出来了,影响的那可是模块和模块之间的调用。如果代码模块没有封装好,更新、更换起来就会特别麻烦。
实现了高内聚低耦合,就能保证代码模块有良好的设计。万一需求变更了,那就会出现两种情况:要么是代码模块本身可以不变,变的是模块互相之间的拼接,就像是乐高积木一样;要么是某个模块性能跟不上了,那只需要更新这个模块里面的程序,不会影响其他模块里的代码。前面讲的给台式电脑换CPU就有点这个意思。
不论需求是不是会发生变化,当一个程序员在按照高内聚低耦合编写自己程序的时候,其实就已经在为变更做准备了。
不只是高内聚低耦合,还有一个原则也是程序员实现软件要遵守的。这就是代码模块要尽可能地复用。比如说,你用一个代码模块实现了搜索功能,可以快速地把符合条件的内容找出来,那么不论是在做电子书内容的搜索,还是网页内容的搜索,就别再重新发明轮子了,需要搜索的时候调用同一段代码就可以了。
在应用复用原则的时候,普通人或是菜鸟程序员可能都会误解,感觉复用就是程序员的合理“偷懒”,已经有了的功能就不用再做。其实,对复用来说,节省成本只是附带结果,它更重要的功能是管理一致性。
什么意思呢,就拿前面搜索功能来说吧。如果这个功能没有被复用,而是程序员很“勤快”地把它复制了两份,一份用在了电子书搜索功能里面,一份用在了网页搜索功能里面。那要是遇到了变更,比如这个搜索模块的技术落后了,需要用更好的技术增加搜索速度,那最开始的“勤快”就会给变更带来麻烦,必须把两个复制的模块都做同样的修改才行。
而如果这部分代码是被复用的呢,就没有这个麻烦了,只需要一次修改,全部调用这个模块的代码都可以用到最新的搜索技术。而且这还不是麻烦不麻烦的问题,现在普普通通一个软件产品,都可能会有几十万行的代码,需要上百人的协作,如果没有把复用原则执行好,那么就是一个小更改都可能让整个产品出现bug,导致崩溃。
前面讲的这些方法,还都是程序员在编程技术上做出的应对。为了应对变更,程序员高手的方法,可不只是停留在技术层面,他们还会在组织团队和制定流程的层面上给出自己的解决方案。就比如,站立会议,这是在很多敏捷开发方法里都会用到的技术。
你别觉得站立会议只是把原来坐着开会变成了站着开会,其实这是用站立的方式倒逼缩短开会时间。因为坐着开会比较舒服,不自觉地就会拖延开会时间。保证每次站立会议不会超过15分钟,这样才能让站立会议成为每天的日常会议,每天十几分钟还是可以承受的。这样,团队里面的每个人每天都能和其他人做一次同步,知道其他人都做了什么,项目都发生过什么变化,自己是否需要做相应的调整。
还有看板方法,除了让编程这种看不见工作量的工作变得可见可衡量,它也是所有人同步进度的公告栏。有了看板就不需要刻意通知,团队里的每个人也都能知道项目的进展情况。
其实《敏捷宣言》就是程序员应对变更的指导方针。这个宣言不是我们这次解读的重点了,感兴趣的话,你可以自己去了解相关的内容。
讲到这里,这次解读的核心内容就讲完了。通过前面程序员面对完美和变更的态度,我想你会和我一样有这样一个感觉,高手程序员,都是一群能给自己重新设计议题的人。他们本来是用编程的方法实现用户需求的一群技术人员,但是,他们并没有把自己限定在一个确定框架范围内去解决问题。当他们发现无法获得确定的需求时,不是要求用户或是产品经理必须把需求确定下来,而是跳出编程技术的框架。他们重新定义了产品的价值,产品不再是简单地给用户提供功能,而是变成了探寻用户需求的工具,在更高层上把解决问题掉。
面对持续改善问题时也一样,程序员也没有停留在原有的议题里面,想着怎么能减少变更,而是跳出了原有问题的框架,想着如何可以适应变更。为了做到适应变更,不只是在编程技术这一个层面给出了自己的解决方案,在团队建设和流程制定上也有自己的解决方案,甚至都总结出了相应的指导思想。
所以,如果让我说什么是程序员的务实,我会说,不是死磕、追求完美的精神,而是为了实现最终的目标,总能跳出框架为自己重新设计议题的能力。
撰稿、转述:王木头
脑图:刘艳