购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

第11章
结对编程,你开车,我导航

阿朱与阿捷讨论过持续集成后,第二天就召集所有的人开会,把自己的想法跟大家讲了一下,大家纷纷说好,并当即进行了分工。阿朱和阿紫负责产品自动安装和验收测试中的自动化,大民、小宝负责自动编译、自动UT(单元测试)和自动打包部分,最后再由阿朱进行集成。因为TD的OSS 5.0产品庞大,再加上历史积累下来的回归测试用例很多,大家决定先将持续集成、自动测试的频率设为每天进行一次。大家还为整个过程起了一个很好听的名字AutoVerify,意指自动进行产品的验证。同时,大家还讨论了一些实现细节。

每天下班前,大家把签出的代码签入到代码库中,AutoVerify程序会在每晚8:00从代码库中提取最新的代码,自动进行编译,编译成功后同时启动两个任务:一个进程运行自动UT,另外一个进程进行打包并自动部署到测试环境中。这是因为UT的时间较长,需要两个小时左右才能完成全部的868个测试用例。这样二者并行进行,可以节省时间,多运行一些回归测试用例。虽然也有可能UT测试用例失败,但应该是不影响产品在测试环境下运行的,可以打包并安装。安装成功后,开始自动回归测试。

因为历史遗留的测试用例太多,一个晚上不可能做完所有的测试用例,应该先运行一些核心的、重要的测试用例,这个筛选工作由阿紫负责。只有在周末的时候,才把所有的测试用例全部运行一遍。AutoVerify需要自动收集统计信息,比如运行了多少个测试用例,通过率是多少等,把这些结果汇总下来。第二天早上9:00,AutoVerify自动把晚上自动验证的结果通过邮件发给阿朱和阿捷,由阿朱负责检查。为了减少垃圾邮件,只有当任何一个环节出现问题的时候,AutoVerify才会把邮件发给大家。此时,阿朱负责把出错日志转给相应的人,收到该邮件的人要第一时间解决。

在讨论完AutoVerify后,大民利用剩余的时间,把XP提上了议事日程。

这次,“我们一次性地用到了XP的两个重要实践:持续集成和自动化测试。其实,XP还有其他一些很好的实践,有些已经通过Scrum这个框架体现出来,譬如小发行版(Small Releases)等,XP还有一些编程实践也是值得我们尝试的。”

“嗯,我赞同这个观点。Scrum本身没有规定具体的编程实践,我们正好可以用XP来补充!大民,你接着说说适合我们自己团队的XP实践吧!”阿捷说。

“第一个应该是结对编程,其次是编码标准、简单增量设计、重构和测试驱动开发等,还有代码集体所有权。”

小宝插了一句:“关于代码集体所有权,其实咱们已经做得很不错了。大家看,咱们因为模块多,代码多,一直也没有也不太可能规定具体哪块的代码归谁拥有,而是任何一个人都有权修改任何一段代码。谁破坏了某个模块,谁要负责进行修补。”

“嗯,这点我赞同。不过我想强调一下,我们应该继续保持这个优良传统!同时因为代码归集体所有,所以大家就都要遵循统一的编码标准才行。”

“没错,这个项目遗留下来的代码太多太杂,这里面既有美国老哥写的,还有印度兄弟写的,苏格兰兄弟写的,再加上咱们自己写的。真够百花齐放的!”

“是啊!短时间内,我们是不可能把所有的代码都统一起来。虽然也有一些类似aStyle等自动化代码美化工具,可以一次性地把所有代码整合成符合统一编码标准的形式,但这样做的风险实在太大!万一出了问题,所有的代码都被改动了,反而没办法跟踪,不容易解决。”大民显然仔细分析过这个问题。

小宝点了点头:“我想我们可以一步一步来,只有当我们需要改动哪个文件时,才对该文件按照编码标准进行一次优化。不过话又说回来,我们现在的编码标准有点乱,也有点过时了,需要重新整理一下才行。”

“要不这个任务就交给你?”阿捷问。

“行啊!其实我已经整理一半了。”小宝的积极主动性还是挺高的,“我们原来有一个基础版本,但有些东西已经过时了,另外还要加些新的规则进来。”

大民接过话头:“关于增量设计和重构这块我们做得还不够,当然,这也是有历史原因的。咱们以前一直都是瀑布式开发,而瀑布式开发非常重视设计。仅仅针对设计,咱们以前的流程就会产生概要设计文档、外部接口文档、详细设计文档、测试策略、测试计划等,从敏捷的角度来讲,我们应该做一些简化。”

“嗯,是有必要精简,但应该精简到什么程度呢?”阿朱问道。

“我觉得……”大民稍微顿了一下,似乎是故意为了强调,“够用就可以了!就是说不应该太多,但也不能没有。我们需要找出对我们真正有用的文档,真正值得花精力的文档,然后做增量设计。”

“话虽如此!问题是咱们在大流程上还必须按照公司的产品生命周期走,这中间会涉及很多的里程碑,而每个里程碑都要求有完备的文档,才能通过检查,进入下一阶段。”阿朱接着说。

“那我们先来看一下公司的PLC(Product Life Cycle)好了。”阿捷边说边在白板上画出公司的产品生命周期。

“虽然整个周期很长,但咱们必须通过的检查点只有DEV(开发)和SHIP(交付)。咱们团队目前自己实施敏捷开发,也就是在DEV到SHIP之间。其实,这也正是敏捷软件开发与CMMI/ISO 9000等流程相互补充的最有效方式。其间的SQ虽然很重要,但不是必需的,公司强制的并不严。所以咱们只要在DEV和SHIP这两个检查点上提供完备的文档就可以了。”

“DEV在我们开发的启动之初,可以周旋的余地不多,这个念头就不用想了,该准备的文档还要准备好。不过,这个检查点更多的是针对市场部、产品规划部等除研发以外部门的,对于我们研发部门来讲,只需要给出一个项目计划文档和一个软件总体架构文档即可,所以问题不大。而SHIP是在后期,可操作的余地比较大。”

“这样的话,我们是完全可以按照尽量简化、增量设计的思路来做的!在每一个Sprint,我们都只做简单设计,产生对于当前Sprint所必需的文档,而没必要一次性给出大而全的设计方案,一次写出非常完备的文档来。这样也不现实,因为最终还是要不断地修改的。可以通过后继的Sprint,不断完善,不断重构,直至产品发布前,给出最终版本。当然,每次的设计都应该是可以扩充的,而不是走入死胡同,无法重构。大家觉得如何?”

“应该是可以做到的。关键还是度的问题。设计要适度,文档要适度,不能成为我们工作的累赘,又要做到出现争议的时候有据可查。我觉得有些文档还是一开始就要有的。”大民回应道。

“可哪些文档是必须要有的呢?”小宝还是很关心具体的东西。

“在我看来,至少有两份文档是必需的:产品定义文档PRD和概要设计。PRD的目的是告诉大家,我们开发的软件要做成什么样子、要实现哪些功能,这份文档应该是经常更新的,记录开发过程中最新达成的结论。而且这个还必须跟Product Backlog对应起来。概要设计是确保大家在XP的过程中不会脱离轨道,天马行空。”

“嗯,那我们就先按照这个思路实行一段时间。可以通过每次的Sprint回顾会议进行调整。那我们再来看看TDD?”阿捷把头转向大民。

“好!从它的英文Test-Driven Development即可以看出是测试驱动的。也就是说是在开发功能代码之前,先编写单元测试用例代码,由测试代码确定需要编写什么产品代码。这一点与我们大多数人日常的实践是不同的。我们虽然也有UT(单元测试),数量也很多,但这些UT用例基本都是在编写完功能代码之后,才编写的。”

“我觉得区别不大啊!最终都是为了验证功能的正确性。”小宝说。

“不一样!事后的单元测试较TDD会失去大半的意义。我们先来看看通用的测试驱动开发基本过程。”大民边说边把每一步列在白板上。

1. 明确当前要完成的功能。可以记录成一个TO DO(待办)列表。

2. 快速完成针对一个功能的测试用例编写。

3. 测试代码编译通过,但测试用例通不过。

4. 编写对应的功能代码。

5. 测试通过。

6. 对代码进行重构,并保证测试通过。

7. 循环完成所有功能的开发。

大民转过头来,指着刚刚写完的7条说:“乍一看,似乎也没什么。但深奥之处就在于第一步的明确上。如何明确?通常由业务、测试、开发进行一次讨论,就要完成的功能的验收条件达成一致并形成记录,然后测试人员设计并编写验收测试用例,开发人员编写单元测试和并实现功能代码。这样,测试人员早期介入,从而可以避免开发人员与测试人员理解不一致,产生争执并阻塞等待业务分析人员或者行政主管的仲裁。”

“嗯,测试就是应该越早介入越好!是吧,阿紫?”阿朱征求阿紫的支持,阿紫很快点头回应。

“对于开发人员来讲,可以强迫他从测试的角度来考虑设计,考虑代码,这样才能写出适合于测试的代码。”大民接着讲。

“从另外一个角度上说,坚持测试优先的实践,可以让开发人员从一个外部接口和客户端的角度来考虑问题,从而保证软件系统各个模块之间能够较好地连接在一起;而开发人员的思考方式,也会逐步地从单纯的考虑实现,转移到对软件结构的思考上来。这才是测试优先的真正思路。”

“另外,大家看第6步,这里提到了重构。重构是XP里面非常重要的一个实践,只有不断地重构,才能改善代码质量、提高代码复用,它跟TDD/简单增量设计是相辅相成的,谁都离不开谁。那究竟什么时候该重构,什么情况下应该重构呢?”大民把问题抛给大家。

“有新功能的时候重构。”

“需要复用代码的时候重构。”

“该重构时重构。”

“写不下去的时候重构。”

“下一次迭代时重构。”

大家七嘴八舌地回答。

大民看到大家差不多说完了,清了清喉咙:“这些想法基本都对。在TDD中,除去编写测试用例和实现测试用例之外的所有工作都是重构。所以,没有重构,任何设计都不能实现。至于什么时候重构嘛,还要分开看,我的经验是,实现测试用例时重构代码,完成某个特性时重构设计,产品的重构完成后还要记得重构一下测试用例。”

“我刚毕业时,加入了一家铁路部门的信息中心。我很清楚地记得,带我的老师给我的第一句忠告就是‘如果一段代码还能工作,没有出现问题,就不要动它’,因为我们做的是铁路调度实时运维系统,不能出一点差错。”阿捷喝了口水,接着说,“我觉得非常有道理,一直也是奉行这个‘金科玉律’的。你觉得呢,大民?”

大民没有马上回答,沉思了一下:“或许在你们的那个环境那种条件下,这样做是最稳妥的。我想,你们之前肯定因为修改过代码而导致重大错误,从此以后一朝被蛇咬,十年怕井绳,对代码产生了恐惧感,最终无法掌控代码。我是这样认为的,如果一个系统一直没有新的需求,使用的情形一直不变,这样做是可以的。但对于95%的产品而言,是需要不断变化的。如果一些冗余代码、拙劣的代码,存在糟糕的结构和投机性设计,虽然能够正常运行,但这样的软件,常常会带来更大的潜在问题。对于一个负责任的程序员来讲,是不能容忍的。一定要重构,重新优化,夺回对代码的控制权,千万不能滋生得过且过的思想!”

阿捷带头鼓起掌来,大家纷纷响应。大民不好意思地咧着嘴笑了。

等大家静下来,大民接着说:“重构不可避免地会带来一些问题,我们需要建立一个很好的机制保障重构的正确性。其中很重要的一个实践就是单元测试。虽然一些简单的重构可以在没有单元测试的情形下进行,重构工具与编译器自身也提供有一定的安全保障,但如果只采用传统方式对代码进行测试,例如使用调试器或执行功能测试,这种测试方法不仅效率低下,而且是乏味的、不值得信赖的。重构时,代码较以前对修改更为敏感与脆弱。若要避免不必要的问题,则应添加单元测试到项目中。这样可以确保每一小步的重构,都能够及时发现错误。”

“这么看,似乎通过TDD就可以发现很多Bug了?因为开发人员跟我们测试人员是按照同样的功能验收条件设计测试用例的。我说的没错吧?大民?”阿紫问道。

“还不能这样说!按照TDD的方式进行的软件开发可以有效地预防Bug,但不可能通过TDD找到Bug。因为TDD里有一个很重要的概念是‘完工时完工’。意思是说,当开发人员写完功能代码,通过测试了,工作也就做完了。你想啊,当开发人员的代码完成的时候,即使所有的测试用例都亮了绿灯,这时隐藏在代码中的Bug一个都不会露出马脚来。即使之前没通过测试,那也不叫Bug,因为工作还没做完。”

“嗯,我明白了!所以还需要我们测试人员同步设计功能测试用例,进行功能验收测试才行。那个阶段,发现的问题才能真正称为Bug。”

大民点了点头,以示认同。

“我有一个问题,我该为一个功能特性编写测试用例还是为一个类编写测试用例?”小宝问道,“因为从我们的代码中,我看到UT测试用例都是类和方法。”

“这个问题很好!我以前也有过这样的困惑。关于TDD的文章大多都说应该为一个功能特性编写相应的Test Case(测试用例)。后来看了一篇博客文章,才明白是怎么回事。他们在开发一个新特性时,先针对特性编写测试用例,如果发现这个特性无法用测试用例表达,那么将这个特性细分,直至可以为手上的特性写出测试用例为止。然后不断地重构代码,不断地重构测试用例,不断地依据TDD的思想往下做,最后当产品伴随测试用例集一起发布的时候,他们发现经过重构以后的测试用例,就已经和产品中的类/方法一一对应啦!”

“哦,是这样。”小宝看上去还是半信半疑。

“我感觉从功能特性开始是最安全稳妥的方式,这样不会导致任何设计上重大的失误,也符合简单增量设计、不断重构的XP原则。”大民加上一句,进一步澄清着小宝的迷惑。

“那么TDD到底该做到什么程度,才算结束了呢?重构总是无止境的。是通过所有的UT测试用例吗?”小宝问道。

“很简单!Clean Code That Works。”大民抛出来一句英文,看来真的想把大家绕晕才甘心。

“那到底啥意思啊?你还是说中文吧,听不懂你说的Chinglish。”阿紫打趣道。

“这句话是TDD的目标,Work是指代码奏效,也就是必须通过所有的UT测试用例,而Clean是指代码整洁。前者是把事情做对,后者是把事情做好。”

“关于TDD还有什么疑问吗?”阿捷用目光扫了一遍,见没人响应,接着说,“那我们再来讨论一下结对编程吧。上次我们做Scrum发布计划的时候,曾经提到我们缺少一个人,看看能不能从部门内部临时借调一个人过来。现在告诉大家的好消息就是,Charles已经正式批准章浩加入我们团队,进行TD的研发!”阿捷非常兴奋。

还没等阿捷说完,大民就插了一句:“太好了!章浩跟我是前后脚加入Agile的,开发经验很丰富,又熟悉Agile OSS的整个开发环境。嘿嘿,小宝,他来了你再有问题就可以直接向他请教了。章浩人很耐撕(nice)的。”

“是啊!自从Charles那回听过咱们的站立会议之后,对Scrum很有好感。再加上咱们前几个Sprint确实做得还行,所以这次Charles听完我和李沙关于TD项目开发任务的汇报后,咱们不是在资源一栏写着缺少开发人手吗?他看了之后就问我想要谁。我的第一个念头就是章浩!”

“当时说出来还怕Charles不答应,因为章浩毕竟是周晓晓团队的技术带头人。Charles当时可没答应我,只是跟我讲他会去和周晓晓谈谈!谁知道今天早上,Charles就告诉我章浩从下周起暂时借调到咱们团队,做完咱们规划的3个Sprint之后,再看情况是否需要回周晓晓的团队。”阿捷笑着和大家讲述着事情的经过,只是阿捷并没有讲,在和Charles提完借调章浩后,曾经独自找章浩聊过一个晚上。

“哈哈,我说昨天中午在食堂跟章浩一起吃完饭往台球室走,中途遇见周晓晓,周晓晓连看都不看我跟章浩一眼,原来是因为这个啊。下周章浩过来刚好可以赶上咱们的下一个Sprint!”大民兴奋地讲着。

“嗯,是啊。虽然章浩非常有经验,可能他也需要对咱们正在用敏捷方式做的TD项目熟悉一段时间。咱们最好想个办法,让章浩迅速融合到咱们的开发进程中。大民,你还记得刚才你提到的结对编程吗?如果项目组有新人加入,或者由于某种原因进行换岗,你说可以通过结对的方式来提高整个团队的开发效率。今天再给我们大家讲讲吧。”阿捷看着大民。

大民高兴地说:“好啊,阿捷!我老早就想说这个了,只不过咱们组一直都没有进过新人。说到结对,通常咱们大家都会立即想到编程结对,其实在XP中,这个概念可以更宽泛一些,还可以是设计结对、评审结对、单元测试结对!”

“设计结对是在对某个模块开始编码之前,两人共同完成该模块的设计,这种设计通常不会花费很长时间,不会产生设计文档,更多的是讨论交流,主要考虑是否符合总体架构,是否足够灵活,易于重构等。”

“单元测试结对通常是说一个人编写测试代码,另外一个人编写代码来满足测试。这样,任何一个人对设计理解有误,代码都无法通过单元测试,从而避免由同一个人编写单元测试代码和程序代码带来的黑洞,往往可以发现更多的问题或缺陷。”

“听起来是把一个人的TDD,变成了两个人的TDD!”阿捷总结道。

“对!这样效果会很不错!评审结对是在编码活动完成、通过单元测试后进行的。一般采用一个人讲述代码组织和编程思路,一个人倾听、提问的形式。这种评审模式更多地强调了相互交流,这会比一个人单独评审,独立撰写总结评审意见的模式效率要高得多,文档、邮件也减少了。也许有人说,这么做就会没有文档化的评审记录。可谁会关心这个呢?良好的代码应该说明了一切。”

小宝一直听得很仔细,插了一句:“其实,如果两人编程结对了,编程的过程其实也就是复审的过程,完全可以省略评审。”

“对!”大民非常赞同小宝的观点,“设计结对、评审结对、单元测试结对这三种方式是对结对编程实践的有效补充,操作简单,收益却很大。而对真正意义上的编程结对,我其实并不怎么看好!”

“啊?为什么?”大家对大民抛出的这个结论都很震惊。

“编程结对,在任一时刻都只是一个程序员在编程,效率到底有多高呢?1+1>1是肯定了,但是否1+1>2呢?”大民留了一点时间给大家思考。

“现在还没有肯定的答案!国外也有很多关于结对编程的研究,基本都是建立在结对的两人组和一个人之间的对比,结论基本上是结对编程不能始终保证开发质量和效率始终高于单人编程。如果是结对的两人组和两个单人开发组进行对比,结果更是未必。所以,我也不认为结对编程一定能始终提高效率。”

“但是,我觉得,他应该能够在某一个阶段,或者说项目进行的某一个阶段内提高效率提高质量。”小宝满怀疑问地说。

“这也是对的!所以我前面也提到了‘始终’这个词!这是因为只有两个经验相等的人结对才有可能真正提高编码效率。而现实中,经常是一个有经验的人坐在旁边,另一个经验不太丰富的人进行编程,还会有一个老手轮询多个新手进行开发的方式,在国内公司尤其普遍,这样就更难做到1+1>2。”

“通常支持结对编程的人认为,当两个人合作三个月以后,效率才有可能超过两个人单独编程的效率!这里有一个时间前提,三个月以后。三个月这个时间未必是真实确凿的时间分界线,它只是一个模糊的、大概的时间范畴,如果两个人配合得好,也许只需要两个多月,如果配合不好,也许是四、五个月或者更长的时间,不确定性很大。”

“许多时候,如果仅仅只是想减少缺陷的数量,我认为还是设计结对、评审结对、单元测试结对这三种方式更为有效一些。此外,结对编程始终是两个人的合作行为,其效果会受到多种因素影响。譬如,两个人的性格、个人关系、沟通能力、技术是否互补等都会影响最终的结果。究竟1+1大于2还是小于2真的是一个很难说的事情。只能靠团队自己不断地组合,找出合适的配对人选。”

大家纷纷点头,觉得大民说得很有道理!

“那我们就不实践编程结对了?”阿捷问道。

“其实不仅仅编程结对,其他结对实践,也要视人、视项目、视环境而定。至少两个极端情形下,结对毫无益处:第一,需要静心思考的问题。这时完全可以分头行动,等各自有了理解或解决方案再来讨论;第二,琐碎毫无技术含量的工作,不得不手工完成的。这种工作考验的只是耐心,不妨分头行动,效率肯定比结对要高。”

“在有些时候还是可以采用的,特别是对于新加入一个团队的成员而言,可以让他迅速成长,融入团队!因为结对编程的内涵是一种技术、经验、知识的共享,通过共同商讨、解决问题,来降低误解和疏远。但即使是这样的结对,一天中最好也不要超过3小时。”

“嗯,看来我们可以考虑让你跟章浩来一次结对编程!一次Agile老员工的强强联手!哈哈,你跟章浩两个进入Agile的时间加一起超过10年了吧?正邪双修啊。”阿捷很少有这样取笑大民。

“少来这一套,你这家伙,小心下回我修理你。”

在一片笑声中,结束了本次关于XP的讨论。同时也到了下班时间。大家纷纷道别,走出办公室。阿捷并没有马上走,而是独自走回座位,写下了今天的敏捷日记,毕竟今天的讨论太精彩了。

本章知识要点

1. TDD以可验证的方式迫使开发人员将质量内建在思维中,长期的测试先行将历练开发人员思维的质量,而事后的单元测试只是惶恐的跟随者。

2. 重构不是一种构建软件的工具,不是一种设计软件的模式,也不是一个软件开发过程中的环节,正确理解重构的人应该把重构看成书写代码的方式或习惯,重构时时刻刻有可能发生。

3. 软件构建学问中总有一些理论很美好,但是一经使用就可能面目全非,比如传统的瀑布模型。敏捷里有很多被称之为思想的东西,恰恰没有太高深的理论,但都是一些实践的艺术,强调动手做而不是用理论论证。TDD就是这样一种东西,单纯去研究它的理论,分析它的优点和缺点没有任何意义,因为它本身就是一个很单纯的东西,再对其抽象也得不出像“相对论”那样深奥的理论。实践会给出正确的答案。

4. 结对编程不是一种形式化的组合,在实际的XP小组中,结对的双方应该是根据需要不断变换的,应该保证双方都是对这部分工作感兴趣的人,而不是强行指定。

5. 结对编程不是结队编程,是两个人,不是更多。可以扩展到结对设计、结对测试、结对评审。

6. 就像Scrum一样,并不是所有的团队都有能力实行XP,也不是所有的团队都适合实行XP,视实际情况而定。

7. XP中,多数实践方法是互相加强甚至是互相保证的,不能单单拿出某一个实践来单独实施,譬如结对编程,缺乏TDD/重构/简单递增设计等实践的有效补充,结对编程的效果可能会大打折扣。

冬哥有话说

每日构建与持续集成

阿捷团队目前的实践,还是处于每日构建的状态,每日构建有诸多好处,相比传统滞后的集成,已经是前进了一大步。阿捷的团队从每日构建开始,是很明智的选择,下一步就是持续集成。

上一章中提到,每日构建与持续集成的区别在于“频率和反馈两个方面”;每日构建的问题在于,第一天晚上的构建如果失败,第二天还要提交新的代码,然后再等到晚上进行构建,到第三天才能知道结果。如果构建的状态总是失败,团队会习惯于这样失败的状态,长此以往,很容易就恢复到之前的延迟集成的状态。

此外,根据Jez Humble《持续交付》一书中的建议,“下班之前,构建必须处于可工作状态”,这个是有别于每日构建的。有几个原因,第一,持续集成强调的是及时反馈,如果提交代码,构建失败,等到第二天回来再进行修复,记忆已经没有那么清晰了;第二,今日事今日毕,敏捷强调节奏和可工作的软件;第三,如果存在跨时区的团队,一旦构建出现问题,且相关人员已经下班,则对方团队的正常工作就会受到影响。

提交代码触发持续集成,应该留出可能的修复时间。但预留多少时间好呢?太短可能不够修复的,太长又浪费。《持续交付》书中的另外两条规则“时刻准备着回滚到上一个版本”与“在回退之前要规定一个修复时间”,就是针对这种情况。规定一个修复时间,在此时间之前如果还未能恢复持续集成服务器的正常,就进行版本回退,然后进行一次构建和冒烟测试,通过以后才可以下班回家。

持续集成的理念是:如果经常对代码库进行集成对我们有好处,为什么不随时做集成呢?随时的意思是每当有人提交代码到版本库时。

测试驱动开发TDD

Kent Beck说:“当遇到TDD的问题,从来都不是TDD的问题”,是设计的问题,是测试的目标太大。TDD是工程师治愈焦虑的一种方式,分解问题,试验并获得反馈的一种方式。

测试也是一个学习的方式和过程。测试是反馈,因此,测试速度要快,TDD是一个非常漂亮的短循环。

TDD也是一种教育的方式。TDD可以帮助新人快速通过试错取得成长,是一个开发人员积累经验的过程。

代码评审

Kent Beck说:“有Code Review比没有Code Review好,但是没有Code Review比有Code Review好”;这样类似绕口令的一段话,也很有意思。代码评审会造成等待、延迟以及工作切换,阿捷他们正面临这样的问题;但有代码评审总比没有好,虽然你不能指望代码评审发掘太多问题。比代码评审更好的,是通过协作的方式来保障质量,比如结对编程,此时,Code Review的作用就没有那么明显。

也有人说,Code Review的重要性在于社会性意义,当你知道有人会检查你的代码时,你会更认真。对此,我也认同。但是,当知道有人会检查你的代码,可能产生两种心态,一种是正面的,你会更具责任心,一种是负面的,你可能会产生依赖心理。

测试人员对于开发质量也是如此,当知道有专职的测试时,自己少做一个测试,也总会有人在后面把关。

在脸书,所有代码都在公司范围公开,由不同的人维护,每个人都有修改权限,可以push(推送)到不同的代码主干和分支上,并可以部署到生产环境。一个修改,几亿人会使用,成就感极大,压力感会更大,由此转化成了责任感。当开发人员全权负责,没有测试会帮你检查,没有其他人可以依赖,权力越大责任越大,最终的结果却很好。因为这种自由信任的氛围,因为鼓励尝试,允许失败的文化。

关于代码风格等评审,工程师不应该过多地关注编程风格的事情,这些可以使用工具来帮助,这类的小错让机器来解决。工程师应该把精力投入到软件设计和实现这些重要的事情上,允许人来犯更大的错误(做更勇敢的尝试)。

结对编程,是信息沟通与知识传递的过程;两个人的水平,无论是高高,还是高低的搭配,搭配1~2个月,两个人的水平都会提高;此外,如大民所说,虽然结对编程两个人的输出,不会比两个人分别做更高;但是质量会大幅提升,因此带来的整体成本下降;结对是关键,所以未必是编程,大民也提到结对的设计、测试和评审,最关键的是两人互补做出决策,类似于“四眼”原则;结对的角色不必相同,事实上不同角色的结对,更有利于全栈工程师的培养,以及技术的共享;除了设计、开发、测试,建议结对的范围,把运维也拉进来,这样就真正地实现了DevOps里讲的开发与运维的协作。

精华语录

________________________

________________________

________________________ p6k5UU0T4GSssJXM/Qvw5WU+5cBgVQaYYbY5sRMsxP5U+pTl+1zdoS3lO+3Fh/5h

点击中间区域
呼出菜单
上一章
目录
下一章
×