Bob大叔告诉我们,敏捷原本是怎么回事儿

引言

相信每一位码农最美好的梦想,就是产品经理的需求只提一次;项目经理只管嘘寒问暖;所有的技术选型都由自己说了算;老板月月发奖金,年年涨工资……

可惜,这样的梦想等同于幻想。现实是怎样的,就不用再说出来了,码农们心中都有各自挥之不去的痛。因为码农这一职业的共通性,远在大洋彼岸的美国码农们也有着同样的痛。

美国有一位传奇老码农,Robert C. Martin,人称Bob大叔。他的职业生涯始于穿孔卡片机时代,经历了汇编、C/C++、java和C#等语言和开发技术的时代更迭。

Bob大叔在技术上已经炉火纯青,他对软件工程也同样关注。在经历了多年的开发工作后,他恍然明白软件开发是一项不同于工业化生产的劳动,无法做到提前计划并设计好一切。那软件开发的痛点要如何解决呢?

带着对这个问题的思索,他于2001年组织了一场会议,汇聚了当时最优秀的一群头脑,对软件工程的话题进行讨论。会议后敏捷被正式提出,并对其后二十年的软件工程实践产生了深远的影响。

如今敏捷已经成为一项运动,任何一家互联 科技公司或软件企业,都会声称自己或多或少地进行敏捷实践。但这当中却又有人把敏捷做成了教条,或者把敏捷当成软件开发的万能药。

这些企业组织一旦在实践中遭遇挫折,就又对敏捷产生动摇和怀疑。Bob大叔也深知业界出现的这些问题,于是特为此编写了一本书《敏捷整洁之道:回归本源》,以此为敏捷正本清源,分享他的真知灼见。

现在,就让我们从2001年的那场会议说起吧。

Bob大叔

《敏捷宣言》的诞生

2001年2月11日,犹他州雪鸟镇,这是一个美丽的滑雪度假胜地。一场气氛轻松的会议在此地召开,参会的有17人,这些人都是当时软件工程领域的翘楚。Bob大叔就是这场会议的主要发起者。

Bob大叔把这些精英们聚在一起,显然不是为了滑雪聊天,而是要讨论一个困扰了大家很久的问题。这个问题就是软件开发这项活动,能不能做到质量有保证,而且还能按时交付?

可能对于行外人看来,这似乎是一个不可思议的问题。因为我们总会以工业化生产来类比,只要知道要做什么,然后提前画好图纸,接下来只要把设计变成产品就好了。怎么还会连质量和时间都保证不了呢?

这和软件自身的特殊性有关。在工业生产中,一项实体产品要被生产出来,必须是每一个细节都明确之后才能下到流水线的。而客户对于软件的需求也许一开始就是模糊的,这个时候如果开发人员仅是根据有限的信息就做一个完整的设计并实施,显然会偏离目标。

这种按部就班像流水线一样的软件开发方式,被称之为瀑布式开发,Bob大叔对此深具疑虑,因为他经历过太多失败的项目。这些项目的管理方法看上去很美好,也符合传统经验,但结果往往是灾难性的。

Bob大叔他们找到了解决之道吗?

其实从严格意义上说,雪鸟会议并没有提出实际可操作的解决方案,而是提出了四项核心原则,也可以称之为软件开发要达到的目标。这就是后来人们所熟知的《敏捷宣言》,其内容如下。

  • 个体和互动高于流程和工具;
  • 可工作的软件高于详尽的文档;
  • 客户合作高于合同谈判;
  • 响应变化高于遵循计划。
  • 当时参会的诸位大神都有自己所推崇的开发方法实践,但对于这四项核心原则都达成了一致。所以在认识敏捷的第一步,我们就要明白,敏捷并不是一套有着详尽说明的规范,照着做就行了。而是不论采取何种方法,最终要实现的是在预估时间内交付质量可靠的软件产品。

    生命之环

    Bob大叔个人非常推崇极限编程理念,因此在本书中,他以极限编程的方法论来阐释敏捷运动。极限编程的英文全称是Extreme Programming,它的缩写XP更广为人知。XP理念由Kent Beck等人提出,它强调小步前进、注重沟通、重视反馈等。

    实际上,在雪鸟会议里,XP团体也是人数最多的。XP的另一位共同提出者Ron Jeffries,绘制了一幅敏捷开发的圈层图,他将其称之为“生命之环”。Bob大叔也将此图作为全书的指引贯穿始终。

    生命之环

    生命之环由三个圈层嵌套而成,最外层对应的是业务实践,它关注的是项目管理的内容。Jeff Sutherland与Ken Schwaber创立的Scrum方法就是这一层最为人知的实践。这二位也是雪鸟会议的参与者。

    中间一层是对团队的开发管理。基于这些原则,可以保证团队内信息充分共享,工作的节奏保持稳定,定期发布可用的工作成果。

    最内一层是对程序员的要求。遵循这些原则可以保证代码始终是被单元测试所覆盖的,代码的可读性高、结构良好。程序员们都了解彼此的工作内容,人力的风险尽可能地被降低。

    接下来对这三项实践分别进行详细说明。

    业务实践

    计划游戏

    项目经理最关心的问题就是功能何时开发完成,在没有什么管理方法的环境里,程序员可能只是凭直觉拍脑门说一个时间。但现实往往很难如愿,以至于项目经理和程序员反目成仇的比比皆是。

    在敏捷的世界里,Bob大叔给出的办法是故事加上点数。故事在敏捷中是一个术语,表示软件的一个需求,通常这个需求是独立的,不依赖其他故事。

    每个故事可以赋予一个点数值,表示的是故事的实现难易程度。这个点数以一组数字标示,例如1表示简单,3表示中等,7表示困难,11表示地狱等。

    在计划阶段先头脑风暴出一系列的故事,然后标上点数,接着挑选最重要的故事进行开发实现。当经过数次迭代后,各级点数对应的完成时间也就有了数据统计。

    基于这些数据可以计算出团队的开发速率,这样再对接下来的开发任务估算工期时,就有了比较靠谱的依据。

    所以说,如果在敏捷的框架内将计划游戏做好,项目经理和程序员也会有握手言欢这一天的。

    小步发布

    在瀑布式开发过程中,软件发布是流程末尾的事情。但这样的风险在于,如果存在致命的隐患,到最后一天才被发现,那么要进行返工修补将要付出极其巨大的代价。

    要避免这种情况的发生,频繁地发布是一种解决之道。软件每进行一次发布,都要进行验证,或者要求客户确认功能是否达到要求。

    这样,如果发生误解需求的情况,那么就能够及时调整返工,产生的代价必定远小于在最后一天面对这个问题。

    要做到小步发布,需要自动化技术的支撑,例如自动化测试、持续集成、持续部署等。

    验收测试

    这一步在敏捷的理念中总是被人误解,且实践起来也容易出现混乱。因为这提出了一个颇高的要求,就是业务方要说明需求的具体规格。

    在许多公司里,编写这类文档的是测试人员,而不是业务人员。测试人员会按照自己的理解编写需求规格,然后照此去验收软件产品。

    但客户才是最后真正使用软件的人,他们如果一直不参与迭代并反馈意见,那么他们也将很难得到自己想要的东西。

    Bob大叔的建议是,业务方与测试人员在编写代码之前,就要编写好形式化的测试,描述每个用户故事的行为。而开发人员则将这些测试进行自动化处理。

    这需要业务方、开发人员、测试人员多方沟通协作。尽管这是一个看起来很好理解的道理,但其难点在于执行时各方会因过分关注自己的工作,而导致沟通不畅产生额外的代价。

    因此,这尤其需要程序员与测试人员能够站在更高的维度看待问题,打破沟通的壁垒,共同将验收测试这件事情做好。

    完整团队

    这个概念中虽然包含了“团队”二字,但并没有放在团队实践的圈层中,而是放在了最外层业务实践中。这要从完整团队的意义说起。

    完整团队的含义是软件产品相关的所有角色都要囊括在内。这不仅仅是“客户+程序员”的组合,而是产品经理、项目经理、测试人员、运维人员等,都是团队成员。

    这条实践有着仪式化的要求,就是上述人等在物理距离上也最好为零,大家全都在同一个空间里办公。Bob大叔也解释了这样做的诸多好处,例如可以迅速解决问题,信息充分共享等。

    但在当前的商业环境中,这样做显然要在人力成本上付出相当大的代价,一般的公司未必能够承受得起。退而求其次,借助实时在线交流工具,能够把团队整合起来也是可行的。

    完整团队最终的目标,是要在业务层面统合人力资源,减少不必要的沟通成本,出现问题快速解决。这一实践因此也被放在了业务圈层中。

    团队实践

    隐喻

    我们在组建开发团队的时候,最理想的情况是所有成员既是技术专家,也是业务能手。但这显然有些不切实际,程序员掌握的是技术,而客户最清楚业务。这二者之间的鸿沟如何逾越呢?

    隐喻实践,就是在技术和需求之间架设一座桥梁。例如,程序员在向客户解释数据缓冲设计方案时,大多会采用蓄水池这样形象的比喻,以此达到准确的理解。

    当然,这条实践并不是随心所欲地创造各种类比事物。Bob大叔在书中明确表示“团队需要一个受限制的,有纪律的词汇表。”

    可见,隐喻实践是有严格要求的,团队成员间要就此达成共识,并且能够准确传达出业务和技术的共性。

    可持续节奏

    即使不是专业运动员,我们也知道如果参加马拉松比赛,发令枪响之后就冲刺跑出去的肯定坚持不了多久。可是在软件工程的实践上,这种盲目冲刺的现象随处可见。

    公司固然有业绩的压力,但并不是说强制程序员996甚至007就能保证工程的质量了。把八小时工时延长到16小时就能有两倍产出吗?现实情况是也许八小时能完成的事情也都做不到了,人毕竟不是机器,何况就算是机器也需要保养的。

    所以,团队能够保持一个稳定的节奏前进,这是最重要的。可持续节奏的实践关键之处,就在于团队需要合理分配精力资源,总是以最好的状态去进行工作。

    代码集体所有

    在许多公司里代码是被赋予了不同访问权限的。Bob大叔则对此种做法心存疑虑。因为它会人为地制造出政治阶层,人们会评判拥有哪些代码的团队更得老板青睐。所以,首先就要打破代码权限制造出来的藩篱。

    这条实践从字面上来看,会被理解成所有人都可以看到全部代码。这样理解其实并不完整,因为人人都能看到代码,并不意味着人人都清楚代码的功能。

    在敏捷实践中,对于团队有一个要求,就是团队必须是多功能的。这意味着团队成员不再区分谁是前端开发,谁是后端开发,谁又是DBA。每个人都要清楚其他人的工作,并且可以对他人的工作成果进行完善和修改。

    因此,代码集体所有强调的是,团队要达到多功能的要求,成员间就要有充分的交流和信息共享。代码是集体所有,并不仅是集体可见。

    持续集成

    现在很多作坊式的开发团队里,对于开发管理仍然是一股草莽之气。一种常见的情况是,程序员不断地将写出来的代码上传到源码托管服务,直到发布日期临近时才做第一次编译工作。至于测试,则更是手忙脚乱了。

    这看起来就显得危机四伏,那么何时才是编译代码并进行测试的合适时机呢?答案是当代码签入源码托管服务的那一刻起。

    这就是持续集成实践的要义。通过自动化工具的辅助,实现持续构建,就成为了持续集成的工作基础。哪怕是最细微的代码变动,都会触发系统进行一次构建。构建工作包括编译、自动运行所有单元测试与验收测试。

    持续构建永远不能被破坏。即只要构建失败,那么就必须当场解决问题,保证构建成功通过。这是持续构建铁的纪律,有时候对违反者施以象征性的惩罚,也有助于强化这条纪律。

    持续集成能将构建过程中的风险降至最低,给予团队充分的信心,也保障了软件产品的质量。

    技术实践

    测试驱动开发

    在有多年编程经验,而又初次接触这条实践的程序员看来,这无疑有些疯狂。因为测试驱动开发要求先写一个失败的测试,然后再写一段业务代码让测试通过。

    但Bob大叔作为一名骨灰级的老码农,对TDD的看法却是相当值得借鉴的。因为很难再到几个比他资历更深的人了。

    Bob大叔在经过学习与实践之后,表达了对TDD方法的青睐。使用TDD开发,一是可以有效减少通过调试器去查找问题的时间。调试过程序的码农们都知道,打开调试器茫然地打断点、单步跟踪、查看变量值是件多么痛苦的事情。

    二是TDD过程中产生的单元测试集,这就是一份天然的代码基本功能说明书。因为它是最详细的,也是最即时的,对于代码的阅读与维护起到了巨大的作用。

    TDD的规则列举如下:

  • 先编写一个因为缺乏生产代码而失败的测试,然后再编写生产代码;
  • 只允许编写一个刚好失败的测试;
  • 只允许编写刚好能使当前失败测试通过的生产代码。
  • 重构

    重构也是敏捷活动中一个非常重要的实践。Martin Fowler所著的《重构:改善既有代码的设计》一书,可以说是这一技术领域最为重要的著作了。当然,Martin Fowler也是雪鸟会议的参与者之一。

    但重构活动有两个方面最容易被误解。一是重构的作用,二是重构的时机。

    重构的目标很明确,就是改善代码的可读性,使之易于理解。重构并不解决程序的运行性能问题,甚至有可能付出一些轻微的代价。例如增加了函数调用开销、增加新的类以隔离一些操作等。

    所以,重构是在保证现有代码功能不变的情况下,修改代码结构,使其可维护性更好。

    对于重构何时进行,Bob大叔的建议也非常明确,那就是随时进行。最好是在TDD的步骤中增加一步,即写完了使测试通过的代码,立即进行重构。

    有人会说,时间太紧,先完成全部功能吧,系统上线以后再慢慢对代码做重构。在这种情况下,重构工作也许永远都不会进行。因为一堆糟糕的代码运行在线上,恐怕已经把人压得喘不过气了。

    软件项目的工期永远是紧张的,专业的程序员会为自己负责,交付最好的成果。一名总是产出高质量代码的程序员,他也能获得更多的信任,在工期的制定上也会有更多的话语权。

    简单设计

    Bob大叔认为,简单设计实践也是重构的目标之一,他对此的理解是“仅编写必要的代码,使得程序结构保持最简单、最小和最富表现力。”

    Kent Beck制定的简单设计规则:

    1. 所有测试通过;
    2. 揭示意图;
    3. 消除重复;
    4. 减少元素。

    简单设计的目的是要降低代码的设计重量。一个包含了诸多功能的系统,其设计如果过于复杂,那么就会让程序员的认知负担过重。这样,程序员就要花许多时间和精力,去理解系统的功能是怎样的。

    http协议的设计就是一个很好的例子。熟悉http协议的程序员都知道,只要掌握几个关键内容,就知道http协议是如何工作的。http协议能成为web服务的标准协议,其简单的设计与易理解性是非常重要的因素。

    当然,把事情搞复杂有时候还要更容易些,想要将代码写得简单易懂,还要实现强大的功能,这反倒是不容易的。所以,严格遵循简单设计的原则,不断地练习实践,才能够不断精进写好代码的能力。

    结对

    结对编程可以说是敏捷活动中最知名的实践了。甚至有人对敏捷的全部理解,就是“一定要两个人坐在一起写代码”。这其实是对结对的一种误解,也是Bob大叔要正本清源的问题。

    结对编程有两个重要意义。一是将信息饱和度保持在一个高水平;二是实时进行代码审查工作。

    在团队实践中,我们强调了代码集体所有的重要性,那么结对就是实现这个目标的重要方法。结对编程和空战中的双机编队有些类似。双机作战时,分为长机和僚机。长机负责跟踪主要目标,并进行火力打击。而僚机则在一旁观察策应,掩护长机。

    结对编程时,也是一位程序员掌控键盘,另一位则观察并提出建议。当完成了一些功能后,角色再互换,又按照此种模式进行工作。

    在软件开发的过程中,并不要求每时每刻都进行结对编程,而是可以根据需要来进行。例如系统核心功能的开发、优化算法提高性能,以及查找问题排除bug。

    即使没有听说过结对的程序员,其实在工作中大多也实践过。就是遇到自己写的代码怎么也运行不对的时候,往往会拉着别的同事一块阅读代码。结果同事一眼就看出是哪个变量名打错了,或是逻辑不对头。这种合作,就是在实践结对编程。

    结语

    现在再看敏捷,我们已经知道它不仅是站着开会,或者两个人坐一起写代码。敏捷本身,就像Bob大叔所说,并不是什么全新的事物,在人类长久的生产生活中,就已经包含了敏捷的思想和实践了。

    但软件开发活动,因为其需求变化的多样性、系统所具有的复杂度,以及对智力密集的要求,造成软件产品总是难以按期完工,同时质量也没有保证。这些问题迫使行业里最聪明的大脑进行思考,于是催生出了敏捷运动。

    雪鸟会议之后,敏捷在有些人那里成了软件工程的“银弹”,恨不得像素级的复制,以求一劳永逸地解决问题;而在另一些人那里则成了“噱头”、“形式主义”、“自吹自擂”,以至于谈敏捷而色变,避之唯恐不及。

    产生这两种反应的根源,无外是寄望太高,或者听风就是雨。都是欠缺思考所导致的,其实敏捷提出的是原则和目标,然后才是我们应该怎么做的问题。很多人一上来不思考根本的问题,就急着要找到称手的工具,这显然是本末倒置了。

    Bob大叔以其多年的思考与实践经验,将心得体会提炼结晶在这本 《敏捷整洁之道:回归本源》中。他以雪鸟会议发起者的身份,回顾并阐释了敏捷运动的要义与重要实践方法,为我们提供了相当重要的参考。

    希望程序员们都能够:理解敏捷,学习敏捷,实践敏捷!

    声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

    上一篇 2020年6月23日
    下一篇 2020年6月23日

    相关推荐