当前位置: 100md首页 > 医学版 > 医学资料 > 资料下载2021
编号:7037
高效程序员的45个习惯全文.pdf
http://www.100md.com 2021年1月27日
第1页
第5页
第18页
第23页
第46页
第122页

    参见附件(2602KB,171页)。

     高效程序员的45个习惯全文

    45个习惯,分为7个方面:工作态度,学习,交付软件,反馈,编码,调试,协作。每一个具体的习惯里,一开始提出一个谬论,然后展开分析,之后有正队性地提出正确的做法,并设身处地地讲出了正确做法给你个人的“切身感受”

    编辑推荐

    十余年来,软件行业发生了翻天覆地的变化。敏捷方法大行其道,测试和测试驱动开发在很多开发人员的工作中扮演着重要的角色。作为一名程序员,你应该培养怎样的素质,方能对多变的环境应对自如,始终立于不败之地?

    本书简明实用、见解深刻,总结了高效程序员在开发过程中的45个个人习惯、思想观念和方法,有助于开发人员在开发进程、编码工作、开发者态度、项目和团队管理以及持续学习这5个方面积极修炼。

    通过学习这些内容,养成这些好的习惯,你可以极大地提升自己的编程实力,更快速、更可靠地交付更高质量的软件,从而成为真正的高效程序员。

    相关内容部分预览

    内容简介

    《高效程序员的45个习惯:敏捷开发修炼之道(修订版)》总结并生动地阐述了成为高效的开发人员所需具备的45个习惯、思想观念和方法,涵盖了软件开发进程、编程和调试工作、开发者态度、项目和团队管理以及持续学习等几方面。

    《高效程序员的45个习惯:敏捷开发修炼之道(修订版)》适合所有程序员阅读。

    作者简介

    Venkat Subramaniam博士

    Agile Developer公司创始人,敏捷开发权威人士。他培训并指导了美国、加拿大、印度和欧洲多国的上千名软件开发人员,并多次在各种大会上发表演讲。他还是.NET Gotchas的作者。

    Andy Hunt

    敏捷开发权威人士,敏捷宣言的创始人,Pragmatic Programmers公司创始人。除了本书,他还著有《程序员修炼之道——从小工到专家》等多本备受读者好评的图书。

    钱安川

    汇银丰网络总裁助理兼CTO。专注于O2O、移动互联网领域的产品和管理。曾任ThoughtWorks公司软件咨询师、敏捷过程教练。

    郑柯

    从事 IT 行业十余年,历任程序员、项目经理、市场、销售,曾混迹于《程序员》、InfoQ 等技术媒体。现自由职业,以艺术普及为毕生志业,独立创办、运营“一天一件艺术品”

    读者评价

    主要讲述了程序员在敏捷开发过程中的工作态度,自我学习,软件交付,编码,调试,团队协作,自我管理等方面,总结为45个习惯,基本是敏捷技术实践和管理实践在开发人员身上的应用,亮点是给出以往错误的认识和对比分析,很多实战经验。有敏捷开发经验的人员会感受很深刻,对于传统开发人员感悟会很有限。还是非常推荐

    秀程序员应具备哪些良好习惯

    1.-你好stackoverflow,github,谷歌代码,quora,google

    2.经常参与或开创开源项目,做贡献

    3.平均每半年学习和了解一种新框架或工具(纯个人建议,只是了解不需要到精通的程度),与时俱进

    4.平均每天都要写100行代码

    5.写博客(最好是英文博客),乐于分享和交流,如果能每周都写就很不错了。

    6.每周看黑客新闻,techcrunch,51cto,程序员

    7.每季度至少完成一个产品或项目(不管是工作上的还是个人瞎搞的)

    8.提高人文素养,不是让你看毛片,而是多接接地气,别每天LOL的,伤感情折寿

    9.和团队成员多聊天喝酒,有空陪陪家人朋友,经常锻炼身体,不要老是对着代码和无码LOL

    10.对代码的热情和高清无码一样。

    11.坚持以上

    都坚持下来很不容易,基本上属于神级人物了。

    好吧,我承认其实上面只是我的个人目标,基本上都没能做到(1,6,7算是勉强做到了),只是看到了这个问题总结下,顺便激励自己用。

    高效程序员的45个习惯全文截图

    书名:高效程序员的45个习惯:敏捷开发修炼之道

    作者:Andy Hunt, Venkat Subramaniam

    译者:郑柯, 钱安川

    ISBN:978-7-115-21553-6

    本书由北京图灵文化发展有限公司发行数字版。版权所有,侵权必究。

    您购买的图灵电子书仅供您个人使用,未经授权,不得以任何方式复制和传

    播本书内容。

    我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。

    如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等

    维权措施,并可能追究法律责任。

    目录

    版权声明

    引言

    对本书的赞誉

    推荐序一

    推荐序二

    译者序

    第1章 敏捷——高效软件开发之道

    第2章 态度决定一切

    1 做事

    2 欲速则不达

    3 对事不对人

    4 排除万难,奋勇前进

    第3章 学无止境

    5 跟踪变化6 对团队投资

    7 懂得丢弃

    8 打破砂锅问到底

    9 把握开发节奏

    第4章 交付用户想要的软件

    10 让客户做决定

    11 让设计指导而不是操纵开发

    12 合理地使用技术

    13 保持可以发布

    14 提早集成,频繁集成

    15 提早实现自动化部署

    16 使用演示获得频繁反馈

    17 使用短迭代,增量发布

    18 固定的价格就意味着背叛承诺

    第5章 敏捷反馈

    19 守护天使

    20 先用它再实现它

    21 不同环境,就有不同问题

    22 自动验收测试

    23 度量真实的进度

    24 倾听用户的声音

    第6章 敏捷编码

    25 代码要清晰地表达意图

    26 用代码沟通

    27 动态评估取舍

    28 增量式编程

    29 保持简单

    30 编写内聚的代码

    31 告知,不要询问

    32 根据契约进行替换

    第7章 敏捷调试

    33 记录问题解决日志

    34 警告就是错误

    35 对问题各个击破

    36 报告所有的异常

    37 提供有用的错误信息

    第8章 敏捷协作

    38 定期安排会面时间

    39 架构师必须写代码

    40 实行代码集体所有制41 成为指导者

    42 允许大家自己想办法

    43 准备好后再共享代码

    44 做代码复查

    45 及时通报进展与问题

    第9章 尾声:走向敏捷

    9.1 只要一个新的习惯

    9.2 拯救濒临失败的项目

    9.3 引入敏捷:管理者指南

    9.4 引入敏捷:程序员指南

    9.5 结束了吗

    附录A 资源

    索引

    版权声明

    Copyright ? 2006 Venkat Subramaniam and Andy Hunt.Original English

    language edition, entitled Practices of an Agile Developer:

    Working in the Real World .

    Simplified Chinese-language edition copyright ? 2010 by Posts

    Telecom Press. All rights reserved.

    本书中文简体字版由The Pragmatic Programmers, LLC授权人民邮电出版社

    独家出版。未经出版者书面许可,不得以任何方式复制或抄袭本书内容。

    版权所有,侵权必究。

    引言学有所成,学有所用。

    《提鲁库拉尔》中1330条警句第391条

    提鲁瓦鲁瓦(Thiruvalluvar),印度诗人和哲学家,公元前31年

    几乎每句所谓至理名言都有句意思相反的话与之对应,而且后者也同样在理。

    ——乔治·桑塔耶纳(美国哲学家、诗人)

    对本书的赞誉

    书中“切身感受”的内容非常有价值——通过它我们可以做到学有所思,思

    有所悟,悟有所行。

    Nathaniel T. Schutta,《Ajax基础教程》作者

    在我眼中,这就是一本名不虚传的Pragmatic书架系列里的图书:简短、易

    读、精炼、深入、深刻且实用。它对于那些想要采用敏捷方法的人很有价

    值。Forrest Chang,软件主管

    我从一开始看这本书时就一直在想:“哇噻!很多开发者都需要这本书。”

    我很快就认识到这正是我需要的书,故而向各个级别的开发者强烈推荐。

    Guerry A. Semones,Appistry公司资深软件工程师

    此书通过常理和经验,阐述了为什么你应该在项目中使用敏捷方法。最难得

    的是,这些行之有效的实战经验,竟然从一本书中得到了。

    Matthew Johnson,软件工程师

    我买过Pragmatic书架系列的其他书籍,从中看到过这本书提到的一些习

    惯。但是,本书把这些思想整合到一起,而且用了简明、易懂的方式组织起

    来。我在此向开发新手和想要变得“敏捷”的团队强烈推荐此书。

    Scott Splavec,资深软件工程师

    伴随着敏捷实践席卷了整个行业,有越来越多的人需要理解什么是真正的

    “敏捷”。这本简明、实用的书,正符合他们的需求。

    Marty Haught,Razorstream公司软件工程师和架构师

    也许,你已经听说过了敏捷方法学,并在思索着如何才能每天改进自己的工

    作。我的答案是好好读这本书,倾听这天籁之音,融会贯通这些最佳习惯

    吧。

    David Lázaro Saz,软件开发者

    这是一本深入浅出地讲解敏捷核心实践的书。我最欣赏本书的地方在于,它

    不是在推销一个具体的敏捷方法学,而是把各种敏捷方法中的有效实践有机

    地串联成一个整体。它适合那些渴望快速而可靠地交付高质量软件的人们。

    Matthew Bass,软件咨询师

    这本书是《程序员修炼之道》的完美续篇!

    Bil Kleb,NASA研究员推荐序一

    仅仅还在几年前,XP还被认为是方法异教,FDD属于黑客程序方法。如今,敏捷俨然已经成为主流学说,敏捷方法成为人们学习和讨论的热点。敏捷方

    法的应用也更加广泛,以至于不少外包项目都要求采用某种敏捷方法。它不

    仅仅是小团队和小项目在使用,甚至连微软都开始使用Scrum。

    敏捷已经成为一种炙手可热的新时尚。

    因为火热,各种不同的说法就多起来;因为时尚,原本有些不认同敏捷的人

    也开始追捧起来。人们反复地讨论敏捷方法,涉及从哲学思想到实现细节的

    各个层面。人们不断地推出各种不同版本的敏捷方法,甚至有些方法显得如

    此矛盾、如此不同。

    同时,一些误解也一直在坊间流行。一般误认为敏捷就是快,越快就是越敏

    捷——字典上的名词解释是其依据。岂不知它本来要以“lightweight

    processes”(轻量级过程)命名,只不过有些参会者不喜欢被看做是在拳

    台上跳来跳去的轻量级拳手,所以才用了“敏捷”这个词。还有其他一些误

    解是,敏捷就是只写代码不写文档;敏捷需要重构而无需设计;敏捷迭代就

    是尽量做到最小,以至于一个小时就好几次;敏捷需要天才的程序员才能应

    用,其他人都会水土不服;如此这般。

    可以看到,市面上以敏捷为题目的图书俯拾皆是,似乎软件开发的书不加上

    敏捷这个词就是落伍一样。敏捷体系下存在多种方法,介绍每种方法的图书

    就有一大堆。再加上每种方法采用不同的技术,每本书采用不同的组织形

    式,存在这么多书也不奇怪,就更不用提那些仅仅为了跟风而敏捷的作品

    了。

    面对如此百花齐放、百家争鸣的现象,你该从什么地方开始呢?有没有一本

    图书可以作为入门的第一读物呢?

    这本书就可以胜任这样的角色!

    这是一本很容易理解并掌握,不需要太多基础就可以阅读的书。不管你是开

    发人员,还是管理人员、财务等后勤人员、学生、编程爱好者,只要你对敏

    捷有兴趣,就可以读懂这本书。你不会被众多的概念和曲折的逻辑所迷惑,不会被高难度技巧所困扰。这本书为你打开了了解和学习敏捷方法的一扇大

    门,并指出继续前进的道路。你会很悠闲自在地读完这本小书,然后说:“原来敏捷就是这么一回事

    啊!”

    自由软件顾问 刘新生(ozzzzzz)

    推荐序二

    我很喜欢本书的中文书名——高效程序员的45个习惯,比直译成“敏捷开发

    者实践”含蓄多了。敏捷不是目的,只是手段。只要某个手段适合某个场

    景,有助于提升质量,提高交付能力,提高开发者水平……总而言之,有好

    处的事情,我们尽管做就是了,何必冠以敏捷之名?

    记得第一次读本书还是两年前。这时又细细读来,越来越觉得“习惯”一词

    比“实践”更有味道。所谓“流水不腐,户枢不蠹”,厨房脏了就擦一下,总比满墙都是油烟以后再去清理的代价小得多。有价值的东西——比如回

    顾、测试、重构,一切有利于团队建设、提高生产力的实践都应该频繁且持

    续做,然后日积月累就养成了习惯。

    有些习惯很容易养成,有些则很难。我们大都常常许愿,做计划,比如要做

    一个至少100人同时在线的成熟应用,参加义工活动,每周至少一篇博

    客……然后在计划落空的时候,用各种理由来安慰自己。

    李笑来老师在《把时间当作朋友》一书中提到:“所有学习上的成功,都只

    靠两件事:策略和坚持,而坚持本身就应该是最重要的策略之一。”那么,为什么我们会在某些事情上坚持不下去?或者换个角度来看,哪些事情是容

    易坚持下去的?

    以前我是标准的宅男,CS、网络小说、魔兽世界几乎是休闲的全部,等到后

    来得了腰肌劳损,又得了颈椎病,这才痛定思痛,开始游泳锻炼身体。每天

    游两千米,一个月以后,游泳就成了习惯。再举个例子,我老婆生完孩子以

    后体型变化很大,立志想要减肥。为了坚持下去,她把怀孕前的照片放在电

    脑桌面上,时时督促自己。后来,减肥也就变成了一种生活方式。

    从我的个人体验来看,难以坚持下去的事情,基本都是因为没有迫切的欲望

    和激情。单说锻炼身体,无论是为了减肥、祛病,还是塑形美体等,做这些

    事情至少都有明确的目的,这样才能驱使着人们一直坚持下去。没有动机,没有欲望,哪里来的毅力呢?那么,当我们决定做一件事情的时候,首先就要多问问自己:为什么要做这

    件事情?它所带来的好处是什么?如果不做它又会有哪些坏处?有了清晰的

    目的和思路后再去做事,遇到变化时就知道孰轻孰重,该怎么调整计划,同

    时也不至于被重复和乏味消磨了一时的意气。翻开本书之后,你同样也该对

    自己提问:“为什么要有自动验收测试,有了足够的单元测试是不是就能保

    证质量了?”“写自动验收测试有哪些成本,会带来哪些收益?”只有明白

    了“为什么做”,才能够解决“如何做”的问题。

    本书的两名译者与我都是故交。钱安川是我的同事,是ThoughtWorks资深咨

    询师,有丰富的敏捷实施经验。郑柯与我同是InfoQ中文站敏捷社区的编

    辑,一起翻译过数十篇稿件。他翻译的《项目管理修炼之道》也即将由图灵

    公司出版。这次二人联手的作品,定会给读者以赏心悦目的阅读体验。我有

    幸已经从样章中感受到了这一点。

    希望你能够带着问题,踏上愉快的阅读之旅。

    希望你能够养成好习惯。

    李 剑

    ThoughtWorks咨询师

    2009.10.10

    译者序

    “武功者,包括内功、外功、武术技击术之总和。有形的动作,如支撑格

    拒,姿式回环,变化万千,外部可见,授受较易,晨操夕练,不难熟练。而

    无形的内功指内部之灵惠素质,即识、胆、气、劲、神是也,此乃与学练者

    整个内在世界的学识水平密切相关,是先天之慧根悟性与后天智能的总成,必需寻得秘籍方可炼成。”

    摘自《武林秘籍大全》

    公元21世纪,软件业江湖动荡,人才辈出,各大门派林立,白道黑帮,都欲

    靠各自门派的武功称霸武林。在那些外家功门派(传统的瀑布开发方法、CMM、ISO和RUP等)和非正统教

    (中国式太极敏捷UDD等)当道之际,一股新势力正在崛起——以敏捷方法

    为总称的一批内家功门派。

    下面的歌诀是对内家武功招数的概述:

    迭代开发,价值优先

    分解任务,真实进度

    站立会议,交流畅通

    用户参与,调整方向

    结对编程,代码质量

    测试驱动,安全可靠

    持续集成,尽早反馈

    自动部署,一键安装

    定期回顾,持续改进

    不断学习,提高能力

    上面的每种招式,都可寻得一本手册,介绍其动作要领和攻防章法。几乎每

    个内家功门派都有自己的拳法和套路。

    但,正所谓“练拳不练功,到老一场空”。学习招数和套路不难,难的是如

    何练就一身真功夫。内家功,以练内为主,内外结合,以动作引领内气,以

    内气催领动作,通过后天的修炼来弥补先天的不足。

    本书是一本内功手册。它注重于培养软件开发者的态度、原则、操守、价值

    观,即识、胆、气、劲、神是也。

    敏捷的实践者Venkat Subramaniam和Andy Hunt携手著下此书。望有志之士

    有缘得到此书,依法修习,得其精要;由心知到身知,入筋、入骨、入髓,修炼得道。而后,匡扶正义,交付高质量的软件,为人类造福。

    安 川

    2008年4月于北京第1章 敏捷——高效软件开发之道

    不管路走了多远,错了就要重新返回。

    ——土耳其谚语

    这句土耳其谚语的含义显而易见,你也会认同这是软件开发应该遵守的原

    则。但很多时候,开发人员(包括我们自己)发现自己走错路后,却不愿意

    立即回头,而是抱着迟早会步入正轨的侥幸心理,继续错下去。人们会想,或许差不多少吧,或许错误不像想象的那么严重。假使开发软件是个确定

    的、线性的过程,我们随时可以撤回来,如同谚语中所说的那样。然而,它

    却不是。

    相反,软件开发更像是在冲浪——一直处于动态、不断变化的环境中。大海

    本身无法预知,充满风险,并且海里还可能有鲨鱼出没。

    冲浪之所以如此有挑战性,是因为波浪各不相同。在冲浪现场,每次波浪都

    是独一无二的,冲浪的动作也会各不相同。例如,沙滩边的波浪和峭壁下的

    波浪就有很大的区别。

    在软件开发领域里,在项目研发过程中出现的需求变化和挑战就是你在冲浪

    时要应对的海浪——它们从不停止并且永远变化,像波浪一样。在不同的业

    务领域和应用下,软件项目具有不同的形式,带来了不同的挑战。甚至还有

    鲨鱼以各种伪装出没。

    软件项目的成败,依赖于整个项目团队中所有开发成员的技术水平,对他们

    的培训,以及他们各自的能力高低。就像成功的冲浪手一样,开发人员必须

    也是技术扎实、懂得掌握平衡和能够敏捷行事的人。不管是预料之外的波浪

    冲击,还是预想不到的设计失败,在这两种情况下敏捷都意味着可以快速地

    适应 变化。

    敏捷开发宣言

    我们正通过亲身实践和帮助他人实践,揭示了一些更好的软件开发方法。

    通过这项工作,我们认为:

    个体和交互胜过过程和工具可工作的软件胜过面面俱到的文档

    客户协作胜过合同谈判

    响应变化胜过遵循计划

    虽然右项也有价值,但我们认为左项具有更大的价值。

    敏捷宣言作者,2001年版权所有。

    更多详细信息可以访问agilemanifesto.org 。

    敏捷的精神

    那么,到底什么是敏捷开发方法?整个敏捷开发方法的运动从何而来呢?

    2001年2月,17位志愿者(包括作者之一Andy在内)聚集在美国犹他州雪鸟

    度假胜地,讨论一个新的软件开发趋势,这个趋势被不严格地称为“轻量型

    软件开发过程 ”。

    我们都见过了因为开发过程的冗余、笨重、繁杂而失败的项目。世上应该有

    一种更好的软件开发方法——只关注真正重要的事情,少关注那些占用大量

    时间而无甚裨益的不重要的事情。

    这些志愿者们给这个方法学取名为敏捷 。他们审视了这种新的软件开发方

    法,并且发布了敏捷开发宣言:一种把以人为本、团队合作、快速响应变化

    和可工作的软件作为宗旨的开发方法(本页最开始的方框里就是宣言的内

    容)。

    敏捷方法可以快速地响应变化,它强调团队合作,人们专注于具体可行的目

    标(实现真正可以工作的软件),这就是敏捷的精神。它打破了那种基于计

    划的瀑布式软件开发方法,将软件开发的实际重点转移到一种更加自然和可

    持续的开发方式上。

    它要求团队中的每一个人(包括与团队合作的人)都具备职业精神,并积极

    地期望项目能够获得成功。它并不要求所有人都是有经验的专业人员,但必

    须具有专业的工作态度——每个人都希望尽最大可能做好自己的工作。

    如果在团队中经常有人旷工、偷懒甚至直接怠工,那么这样的方法并不适合

    你,你需要的是一些重量级的、缓慢的、低生产率的开发方法。如果情况并

    非如此,你就可以用敏捷的方式进行开发。这意味着你不会在项目结束的时候才开始测试,不会在月底才进行一次系统

    集成,也不会在一开始编码的时候就停止收集需求和反馈。

    开发要持续不断,切勿时续时断

    Continuous development, not episodic

    相反,这些活动会贯穿项目的整个生命周期。事实上,只要有人继续使用这

    个软件,开发就没有真正结束。我们进行的是持续开发、持续反馈。你不需

    要等到好几个月之后才发现问题:越早发现问题,就越容易修复问题,所以

    应该就在此时此刻把问题修复。

    这就是敏捷的重点所在。

    这种持续前进的开发思想根植于敏捷方法中。它不但应用于软件开发的生命

    周期,还应用于技术技能的学习、需求采集、产品部署、用户培训等方面。

    它包括了软件开发各个方面的所有活动。

    持续注入能量

    Inject energy

    为什么要进行持续开发呢?因为软件开发是一项非常复杂的智力活动,你遗

    留下来的任何问题,要么侥幸不会发生意外,要么情况会变得更糟糕,慢慢

    恶化直到变得不可控制。当问题累积到一定程度的时候,事情就更难解决,最后无法扭转。面对这样的问题,唯一有效的解决办法就是持续地推进系统

    前进和完善(见《程序员修炼之道》一书中的“软件熵”[HT00])。

    有些人对使用敏捷方法有顾忌,认为它只是另一种危机管理 而已。事实并

    非如此。危机管理是指问题累积并且恶化,直到它们变得非常严重,以至于

    你不得不立即放下一切正在做的工作来解决危机。而这样又会带来其他的负

    面影响,你就会陷入危机和恐慌的恶性循环中。这些正是你要避免的问题。

    所以,你要防微杜渐,把问题解决在萌芽状态,你要探索未知领域,在大量

    成本投入之前先确定其可行性。你要知错能改,在事实面前主动承认自己的

    所有错误。你要能自我反省,经常编码实战,加强团队协作精神。一开始你

    可能会觉得不适应,因为这同以往有太多的不同,但是只要能真正地行动起

    来,习惯了,你就会得心应手。

    敏捷的修炼之道下面一句话是对敏捷 的精辟概括。

    敏捷开发就是在一个高度协作的环境中,不断地使用反馈进行自我调

    整和完善。

    下面将扼要讲述它的具体含义,以及敏捷的团队应该采取什么样的工作和生

    活方式。

    首先,它要整个团队一起努力。敏捷团队往往是一个小型团队,或者是大团

    队分成的若干小团队(10人左右)。团队的所有成员在一起工作,如果可

    能,最好有独立的工作空间(或者类似bull pen①),一起共享代码和必要

    的开发任务,而且大部分时间都能在一起工作。同时和客户或者软件的用户

    紧密工作在一起,并且尽可能早且频繁地给他们演示最新的系统。

    ① bull pen原指在棒球比赛中,侯补投手的练习场。——译者注

    你要不断从自己写的代码中得到反馈,并且使用自动化工具不断地构建(持

    续集成)和测试系统。在前进过程中,你都会有意识地修改一些代码:在功

    能不变的情况下,重新设计部分代码,改善代码的质量。这就是所谓的重构

    ,它是软件开发中不可或缺的一部分——编码永远没有真正意义上的“结

    束”。

    要以迭代 的方式进行工作:确定一小块时间(一周左右)的计划,然后按

    时完成它们。给客户演示每个迭代的工作成果,及时得到他们的反馈(这样

    可以保证方向正确),并且根据实际情况尽可能频繁地发布系统版本让用户

    使用。

    对上述内容有了了解之后,我们会从下面几个方面更深入地走进敏捷开发的

    实践。

    第2章:态度决定一切 。软件开发是一项智力劳动。在此章,我们会讲解

    如何用敏捷的心态开始工作,以及一些有效的个人习惯。这会为你使用敏捷

    方法打下扎实的基础。

    第3章:学无止境 。敏捷项目不可能坐享其成。除了开发之外,我们还要

    在幕后进行其他的训练,虽然它不属于开发工作本身,但却对团队的发展极

    其重要。我们还将看到,如何通过培养习惯来帮助个人和团队成长并自我超

    越。

    第4章:交付用户想要的软件 。如果软件不符合用户的需求,无论代码写

    得多么优美,它都是毫无用处的。这里将会介绍一些客户协作的习惯和技巧,让客户一直加入到团队的开发中,学习他们的业务经验,并且保证项目

    符合他们的真正需求。

    第5章:敏捷反馈 。敏捷团队之所以能够顺利开展工作,而不会陷入泥潭

    挣扎导致项目失败,就是因为一直使用反馈来纠正软件和开发过程。最好的

    反馈源自代码本身。本章将研究如何获得反馈,以及如何更好地控制团队进

    程和性能。

    第6章:敏捷编码 。为满足将来的需求而保持代码的灵活和可变性,这是

    敏捷方法成功的关键。本章给出了一些习惯,介绍如何让代码更加整洁,具

    有更好的扩展性,防止代码慢慢变坏,最后变得不可收拾。

    第7章:敏捷调试 。调试错误会占用很多项目开发的时间——时间是经不

    起浪费的。这里将会学到一些提高调试效率的技巧,节省项目的开发时间。

    第8章:敏捷协作 。最后,一个敏捷开发者已经能够独挡一面,除此之

    外,你需要一个敏捷团队。这里有一些最有效的实践有助于黏合整个团队,以及其他一些实践有助于团队日常事务和成长。

    敏捷工具箱

    全书中,我们会涉及一些敏捷项目常用的基本工具。也许一些工具你还很

    陌生,所以这里做了简单介绍。想要了解这些工具的详细信息,可以进一

    步去读附录中的有关参考文献。

    Wiki :Wiki②是一个网站,用户通过浏览器,就可以编辑网页内容并创

    建链接到一个新的内容页面。Wiki是一种很好的支持协作的工具,因为团

    队中的每一个人都可以根据需要动态地新增和重新组织网页中的内容,实

    现知识共享。关于Wiki的更多详情,可查阅《Wiki之道》这篇文章

    [LC01]。

    ② WiKi是WikiWikiWeb的简称,WikiWiki源自夏威夷语,本意是快点快

    点。

    版本控制 :项目开发中所有的产物——全部的源代码、文档、图标、构

    建脚本等,都需要放入版本控制系统中,由版本控制系统来统一管理。令

    人惊讶的是,很多团队仍然喜欢把这些文件放到一个网络上共享的设备

    上,但这是一种很不专业的做法。如果需要一个安装和使用版本控制系统

    的详细说明,可以查阅《版本控制之道——使用CVS》[TH03]或者《版本

    控制之道——使用Subversion》[Mas05]。单元测试 :用代码来检查代码,这是开发者获得反馈的主要来源。在本

    书后面会更多地谈到它,但要真正知道框架可以处理大部分的繁琐工作,让你把精力放到业务代码的实现中。想要了解单元测试,可以看《单元测

    试之道Java版》[HT03]和《单元测试之道C版》[HT04],你可以在

    《JUnit Recipes中文版》[Rai04]一书中找到很多写单元测试的实用技

    巧。

    自动构建 :不管是在自己的本地机器上实现构建,还是为整个团队实现

    构建,都是全自动化并可重复的。因为这些构建一直运行,所以又称为持

    续集成。和单元测试一样,有大量的免费开源产品和商业产品为你提供支

    持。《项目自动化之道》[Cla04]介绍了所有自动构建的技巧和诀窍(包

    括使用Java Lamps)。

    最后,Ship It! [RG05]一书很好地介绍了怎样将这些基本的开发环境实

    践方法结合到一起。

    魔鬼和这些讨厌的细节

    如果你翻翻这本书就会注意到,在每节的开头我们都会引入一段话,旁边配

    有一个魔鬼木刻像,诱使你养成不良习惯,如下所示。

    “干吧,就走那个捷径。真的,它可以为你节省时间。没有人会知道

    是你干的,这样你就会加快自己的开发进度,并且能够完成这些任务

    了。这就是关键所在。”

    他的有些话听上去有点儿荒唐,就像是Scott Adams笔下呆伯特(Dilbert)

    漫画书中的魔王——“尖发老板”所说的话一样。但要记住Adams先生可是

    从他那些忠实的读者中得到很多回馈的。

    有些事情看上去就会让人觉得很怪异,但这全部是我们亲耳所闻、亲眼所

    见,或者是大家秘而不宣的事情,它们都是摆在我们面前的诱惑,不管怎

    样,只要试过就会知道,为了节省项目的时间而走愚蠢的捷径是会付出巨大

    代价的。

    与这些诱惑相对,在每个习惯最后,会出现一位守护天使,由她给出我们认

    为你应该遵循的一些良策。

    先难后易 。我们首先要解决困难的问题,把简单的问题留到最后。现实中的事情很少是黑白分明的。我们将用一些段落描述一个习惯应该带给

    你什么样的切身感受,并介绍成功实施和保持平衡的技巧。如下所示。

    切身感受

    本段描述培养某个习惯应该有什么样的切身感受。如果在实践中没有这样的

    体会,你就要考虑改变一下实施的方法。

    平衡的艺术

    一个习惯很可能会做得过火或者做得不够。我们会给出一些建议,帮你

    掌握平衡,并告诉你一些技巧,能使习惯真正为你所用。

    毕竟,一件好事做得过火或者被误用,都是非常危险的(我们见过很多所谓

    的敏捷项目最后失败,都是因为团队在实践的时候没有保持好自己的平

    衡)。所以,我们希望保证你能真正从这些习惯中获益。

    通过遵循这些习惯,把握好平衡的艺术,在真实世界中有效地应用它们,你

    将会看到你的项目和团队发生了积极的变化。

    好了,你将步入敏捷开发者的修炼之路,更重要的是,你会理解其后的开发

    原则。

    致谢

    每本书的出版都是一项艰巨的事业,本书也不例外。除了作者,还有很多幕

    后英雄。

    我们要感谢下面所有的人,正是他们的帮助,本书才得以问世。

    感谢Jim Moore为本书设计的封面插图,感谢Kim Wimpsett出色的文字编辑

    工作(如果还有错,那肯定是最后一刻的修改导致的)。

    最后感谢所有付出时间和精力让本书变得更好的审阅者:Marcus Ahnve、Eldon Alameda、Sergei Anikin、Matthew Bass、David Bock、A. Lester

    Buck III、Brandon Campbell、Forrest Chang、Mike Clark、John Cook、Ed Gibbs 、Dave Goodlad、Ramamurthy Gopalakrishnan、Marty Haught、Jack Herrington、Ron Jeffries、Matthew Johnson、Jason HiltzLaforge、Todd Little、Ted Neward、James Newkirk、Jared

    Richardson、Frédérick Ros、Bill Rushmore、David Lázaro Saz、Nate

    Schutta、Matt Secoske、Guerry Semones、Brian Sletten、Mike Stok、Stephen Viles、Leif Wickland和Joe Winter。

    Venkat Subramaniam致谢

    我要感谢Dave Thomas,他是我的良师益友。如果没有他的指导、鼓励和建

    设性的意见,本书到现在还只是一个空想。

    我有幸与Andy Hunt合著本书,从他身上学到了太多的东西。他不仅是一位

    技术专家(任何注重实效的程序员都知道这一点),还具有令人难以置信的

    表达能力和优秀品质。我欣赏Pragmatic Programmers出版公司制作本书的

    每一个环节,他们精通很多有用的工具,具有解决问题的能力,而且最重要

    的是,他们有很好的工作态度,正因如此,本书才可以如此顺利地发布。

    感谢Marc Garbey的鼓励。他是一位伟大的朋友,他的幽默和敏捷感染了世

    界上的很多人。我特别感谢那些与我一路同行的俗人们(错了,是朋友们)

    ——Ben Galbraith、Brian Sletten、Bruce Tate、Dave Thomas、David

    Geary、Dion Almaer、Eitan Suez、Erik Hatcher、Glenn Vanderburg、Howard Lewis Ship、Jason Hunter、Justin Gehtland、Mark Richards 、Neal Ford、Ramnivas Laddad、Scott Davis、Stu Halloway和Ted Neward

    ——这些家伙棒极了!我感谢Jay Zimmerman(人称敏捷推动者),NFJS的

    主管,感谢他的鼓励,感谢他给我机会去向他的客户推广我的敏捷思想。

    感谢父亲教会我正确的人生价值观,还有母亲给予我创造的灵感。如果不是

    妻子Kavitha的耐心和鼓励,还有我的儿子们Karthik和Krupakar的支持,我

    就没有今天的一切。谢谢,我爱你们!

    Andy Hunt致谢

    好的,我想现在每个人都被感谢过了。但是,我要特别感谢Venkat邀请我参

    与本书的写作。我不会接受任何其他人要我合作写这本书的邀请,但却接受

    了Venkat的邀请。他最清楚整件事情的经过。

    我要感谢当年在雪鸟聚会的那些敏捷精英们。虽然没有任何哪一个人发明了

    敏捷,但正是通过所有人的共同努力,才让敏捷在当今的软件开发行业中茁

    壮成长,成为一支重要的力量。

    当然,还要感谢我的家人,感谢他们的支持和理解。从最早的《程序员修炼

    之道》到现在这本书是一个漫长的征途,但也是一段开心的经历。现在,演出开始了。

    第2章 态度决定一切

    选定了要走的路,就是选定了它通往的目的地。

    ——Harry Emerson Fosdick(美国基督教现代主义神学家)

    传统的软件开发图书一般先介绍一个项目的角色配置,然后是你需要产生哪

    些工件(artifact)——文档、任务清单、甘特(Gantt)图等,接着就是

    规则制度,往往是这么写的:汝当如此①这般……本书的风格不是这样的。

    欢迎进入敏捷方法的世界,我们的做法有些不同。

    ① 或更通俗地写成:系统应当如何如何……。

    例如,有一种相当流行的软件方法学要求对一个项目分配35种不同的角色,包括架构师、设计人员、编码人员、文档管理者等。敏捷方法却背道而驰。

    只需要一个角色:软件开发者,也就是你。项目需要什么你就做什么,你的

    任务就是和紧密客户协作,一起开发软件。敏捷依赖人,而不是依赖于项目

    的甘特图和里程表。

    图表、集成开发环境或者设计工具,它们本身都无法产生软件,软件是从你

    的大脑中产生的。而且它不是孤立的大脑活动,还会有许多其他方面的因

    素:个人情绪、办公室的文化、自我主义、记忆力等。它们混为一体,态度

    和 心情 的瞬息变化都可能导致巨大的差别。

    因此态度非常重要,包括你的和团队的。专业的态度应该着眼于项目和团队

    的积极结果,关注个人和团队的成长,围绕最后的成功开展工作。由于很容

    易变成追求不太重要的目标,所以在本章,我们会专注于那些真正的目标。

    集中精力,你是为做事 而工作。(想知道怎样做吗?请见下一页。)

    软件项目时常伴有时间压力——压力会迫使你走捷径,只看眼前利益。但

    是,任何一个有经验的开发者都会告诉你,欲速则不达 (我们在第15页将

    介绍如何避免这个问题)。

    我们每个人或多或少都有一些自我主义。一些人(暂且不提他们的名字)还

    美其名曰“健康”的自我主义。如果要我们去解决一个问题,我们会为完成

    任务而感到骄傲,但这种骄傲有时会导致主观和脱离实际。你也很可能见过设计方案的讨论变成了人身攻击,而不是就事论事地讨论问题。对事不对人

    (第18页)会让工作更加有效。

    反馈是敏捷的基础。一旦你意识到走错了方向,就要立即做出决策,改变方

    向。但是指出问题往往没有那么容易,特别当它涉及一些政治因素的时候。

    有时候,你需要勇气去排除万难,奋勇前进 (第23页)。

    只有在你对项目、工作、事业有一个专业的态度时,使用敏捷方法才会生

    效。如果态度不正确,那么所有的这些习惯都不管用。有了正确的态度,你

    才可以从这些方法中完全受益。下面我们就来介绍这些对你大有裨益的习惯

    和建议。

    1 做事

    “出了问题,第一重要的是确定元凶。找到那个白痴!一旦证实了是

    他的错误,就可以保证这样的问题永远不会再发生了。”

    有时候,这个老魔头的话听起来似乎很有道理。毫无疑问,你想把寻找罪魁

    祸首设为最高优先级,难道不是吗?肯定的答案是:不。最高优先级应该是

    解决问题。

    也许你不相信,但确实有些人常常不把解决问题放在最高优先级上。也许你

    也没有。先自我反省一下,当有问题出现时,“第一”反应究竟是什么。

    如果你说的话只是让事态更复杂,或者只是一味地抱怨,或者伤害了他人的

    感情,那么你无意中在给问题火上浇油。相反,你应该另辟蹊径,问问“为

    了解决或缓解这个问题,我能够做些什么?”在敏捷的团队中,大家的重点

    是做事。你应该把重点放到解决问题上,而不是在指责犯错者上面纠缠。

    指责不能修复bug

    Blame doesn’t fix bugs

    世上最糟糕的工作(除了在马戏团跟在大象后面打扫卫生)就是和一群爱搬

    弄是非的人共事。他们对解决问题并没有兴趣,相反,他们爱在别人背后议

    论是非。他们挖空心思指手画脚,议论谁应该受到指责。这样一个团队的生

    产力是极其低下的。如果你发现自己是在这样的团队中工作,不要从团队中

    走开——应该跑开。至少要把对话从负面的指责游戏引到中性的话题,比如

    谈论体育运动(纽约扬基队最近怎么样)或者天气。在敏捷团队中,情形截然不同。如果你向敏捷团队中的同事抱怨,他们会

    说:“好,我能帮你做些什么?”他们把精力直接放到解决问题上,而不是

    抱怨。他们的动机很明确,重点就是做事,不是为了自己的面子,也不是为

    了指责,也无意进行个人智力角斗。

    你可以从自己先做起。如果一个开发者带着抱怨或问题来找你,你要了解具

    体的问题,询问他你能提供什么样的帮助。这样简单的一个行为就清晰地表

    明你的目的是解决问题,而不是追究责任,这样就会消除他的顾虑。你是给

    他们帮忙的。这样,他们会知道每次走近你的时候,你会真心帮助他们解决

    问题。他们可以来找你把问题解决了,当然还可以继续去别处求助。

    符合标准不是结果

    许多标准化工作强调遵从一个过程,按符合的程度作评判,其理由是:如

    果过程可行,那么只要严格按这个过程行事,就不会有问题。

    但是,现实世界并不是如此运行的。你可以去获得ISO-9001认证,并生产

    出一件漂亮的铅线织就的救生衣。你完全遵循了文档中约定的过程,糟糕

    的是到最后所有的用户都被淹死了。

    过程符合标准并不意味结果是正确的。敏捷团队重结果胜于重过程。

    如果你找人帮忙,却没有人积极响应,那么你应该主动引导对话。解释清楚

    你想要什么,并清晰地表明你的目的是解决问题,而不是指责他人或者进行

    争辩。

    指责不会修复bug 。把矛头对准问题的解决办法,而不是人。这是

    真正有用处的正面效应。

    切身感受

    勇于承认自己不知道答案,这会让人感觉放心。一个重大的错误应该被当作

    是一次学习而不是指责他人的机会。团队成员们在一起工作,应互相帮助,而不是互相指责。

    平衡的艺术

    “这不是我的错”,这句话不对。“这都是你的错”,这句话更不对。

    如果你没有犯过任何错误,就说明你可能没有努力去工作。开发者和质量工程师(QA)争论某个问题是系统本身的缺陷还是系统增

    强功能导致的,通常没有多大的意义。与其如此,不如赶紧去修复它。

    如果一个团队成员误解了一个需求、一个API调用,或者最近一次会议

    做的决策,那么,也许就意味着团队的其他成员也有相同的误解。要确

    保整个团队尽快消除误解。

    如果一个团队成员的行为一再伤害了团队,则他表现得很不职业。那

    么,他就不是在帮助团队向解决问题的方向前进。这种情况下,我们必

    须要求他离开这个团队①。

    ① 不需要解雇他,但是他不能继续留在这个项目中。同时也要意识到,频繁的人员变动对整个团队的平衡也很危险。

    如果大部分团队成员(特别是开发领导者)的行为都不职业,并且他们

    对团队目标都不感兴趣,你就应该主动从这个团队中离开,寻找更适合

    自己发展的团队(这是一个有远见的想法,没必要眼睁睁地看着自己陷

    入一个“死亡之旅”的项目中[You99])。

    2 欲速则不达

    “你不需要真正地理解那块代码,它只要能够工作就可以了。哦,它

    需要一个小小的调整。只要在结果中再加上几行代码,它就可以工作

    了。干吧!就把那几行代码加进去,它应该可以工作。”

    我们经常会遇到这种情况,出现了一个bug,并且时间紧迫。快速修复确实

    可以解决它——只要新加一行代码或者忽略那个列表上的最后一个条目,它

    就可以工作了。但接下来的做法才能说明,谁是优秀的程序员,谁是拙劣的

    代码工人。

    拙劣的代码工人会这样不假思索地改完代码,然后快速转向下一个问题。

    优秀的程序员会挖掘更深一层,尽力去理解为什么这里必须要加1,更重要

    的是,他会想明白会产生什么其他影响。

    也许这个例子听起来有点做作,甚至你会觉得很无聊。但是,真实世界中有

    大量这样的事情发生。Andy以前的一个客户正遇到过这样的问题。没有一个

    开发者或者架构师知道他们业务领域的底层数据模型。而且,通过几年的积

    累,代码里有着成千上万的+1和-1修正。在这样脏乱的代码中添加新的功能

    或者修复bug,就难逃脱发的噩运(事实上,很多开发者因此而秃顶)。千里之堤,溃于蚁穴,大灾难是逐步演化来的。一次又一次的快速修复,每

    一次都不探究问题的根源,久而久之就形成了一个危险的沼泽地,最终会吞

    噬整个项目的生命。

    防微杜渐

    Beware of land mines

    在工作压力之下,不去深入了解真正的问题以及可能的后果,就快速修改代

    码,这样只是解决表面问题,最终会引发大问题。快速修复的诱惑,很容易

    令人把持不住,坠入其中。短期看,它似乎是有效的。但从长远来看,它无

    异于穿越一片流沙,你也许侥幸走过了一半的路程(甚至更远),一切似乎

    都很正常。但是转眼间悲剧就发生了……

    只要我们继续进行快速修复,代码的清晰度就不断降低。一旦问题累积到一

    定程度,清晰的代码就不复存在,只剩一片混浊。很可能在你的公司就有人

    这样告诉你:“无论如何,千万不能碰那个模块的代码。写代码那哥们儿已

    经不在这儿了,没有人看得懂他的代码。”这些代码根本没有清晰度可言,它已经成为一团迷雾,无人能懂。

    Andy如是说……

    要理解开发过程

    尽管我们在谈论理解代码,特别是在修改代码之前一定要很好地理解它,然而同样道理,你也需要了解团队的开发方法或者开发过程。

    你必须要理解团队采用的开发方法。你必须理解如何恰如其分地使用这种

    方法,为何它们是这样的,以及如何成为这样的。

    只有理解了这些问题,你才能进行有效的改变。

    如果在你的团队中有这样的事情发生,那么你是不可能敏捷的。但是敏捷方

    法中的一些技术可以阻止这样的事情发生。这里只是一些概述,后面的章节

    会有更深入的介绍。

    不要孤立地编码

    Don’t code in isolation

    孤立非常危险,不要让开发人员完全孤立地编写代码(见第155页,习惯

    40)。如果团队成员花些时间阅读其他同事写的代码,他们就能确保代码是可读和可理解的,并且不会随意加入这些“+1或-1”的代码。阅读代码的频

    率越高越好。实行代码复审 ,不仅有助于代码更好理解,而且是发现bug最

    有效的方法之一(见第165页,习惯44)。

    使用单元测试

    Use unit tests

    另一种防止代码难懂的重要技术就是单元测试。单元测试帮助你很自然地把

    代码分层,分成很多可管理的小块,这样就会得到设计更好、更清晰的代

    码。更深入项目的时候,你可以直接阅读单元测试——它们是一种可执行的

    文档(见第78页,习惯19)。有了单元测试,你会看到更小、更易于理解的

    代码模块,运行和使用代码,能够帮助你彻底理解这些代码。

    不要坠入快速的简单修复之中。 要投入时间和精力保持代码的整

    洁、敞亮。

    切身感受

    在项目中,代码应该是很亮堂的,不应该有黑暗死角。你也许不知道每块代

    码的每个细节,或者每个算法的每个步骤,但是你对整体的相关知识有很好

    的了解。没有任何一块代码被警戒线或者 “切勿入内”的标志隔离开。

    平衡的艺术

    你必须要理解一块代码是如何工作的,但是不一定需要成为一位专家。

    只要你能使用它进行有效的工作就足够了,不需要把它当作毕生事业。

    如果有一位团队成员宣布,有一块代码其他人都很难看懂,这就意味着

    任何人(包括原作者)都很难维护它。请让它变得简单些。

    不要急于修复一段没能真正理解的代码。这种+1-1的病症始于无形,但是很快就会让代码一团糟。要解决真正的问题,不要治标不治本。

    所有的大型系统都非常复杂,因此没有一个人能完全明白所有的代码。

    除了深入了解你正在开发的那部分代码之外,你还需要从更高的层面来

    了解大部分代码的功能,这样就可以理解系统各个功能块之间是如何交

    互的。

    如果系统的代码已经恶化,可以阅读第23页习惯4中给出的建议。3 对事不对人

    你在这个设计上投入了很多精力,为它付出很多心血。你坚信它比其

    他任何人的设计都棒。别听他们的,他们只会把问题变得更糟糕。”

    你很可能见过,对方案设计的讨论失控变成了情绪化的指责——做决定是基

    于谁提出了这个观点,而不是权衡观点本身的利弊。我们曾经参与过那样的

    会议,最后闹得大家都很不愉快。

    但是,这也很正常。当Lee先生在做一个新方案介绍的时候,下面有人会

    说:“那样很蠢!”(这也就暗示着Lee先生也很蠢。)如果把这句话推敲

    一下,也许会好一点:“那样很蠢,你忘记考虑它要线程安全。”事实上最

    适合并且最有效的表达方式应该是:“谢谢,Lee先生。但是我想知道,如

    果两个用户同时登录会发生什么情况?”

    看出其中的不同了吧!下面我们来看看对一个明显的错误有哪些常见的反

    应。

    否定个人能力。

    指出明显的缺点,并否定其观点。

    询问你的队友,并提出你的顾虑。

    第一种方法是不可能成功的。即使Lee是一个十足的笨蛋,很小的问题也搞

    不定,但你那样指出问题根本不会对他的水平有任何提高,反而会导致他以

    后再也不会提出自己的任何想法了。第二种方法至少观点明确,但也不能给

    Lee太多的帮助,甚至可能会让你自己惹火上身。也许Lee能巧妙地回复你对

    非线程安全的指责:“哦,不过它不需要多线程。因为它只在Frozbot模块

    的环境中使用,它已经运行在自己的线程中了。”哎哟!忘记了Frozbot这

    一茬了。现在该是你 觉得自己蠢了,Lee也会因为你骂他笨蛋而生气。

    现在看看第三种方法。没有谴责,没有评判,只是简单地表达自己的观点。

    让Lee自己意识到这个问题,而不是扫他的面子①。由此可以开始一次交

    谈,而不是争辩。

    ① 通常,这是一个很好的技巧:引导性地提出一个疑问,让他们自己意

    识到问题。

    Venkat如是说……要专业而不是自我

    多年以前,在我担任系统管理员的第一天,一位资深的管理员和我一起安

    装一些软件,我突然按错了一个按钮,把服务器给关掉了。没过几分钟,几位不爽的用户就在敲门了。

    这时,我的导师赢得了我的信任和尊重,他并没有指责我,而是对他们

    说:“对不起,我们正在查找是什么地方出错了。系统会在几分钟之内启

    动起来。”这让我学到了难忘的重要一课。

    在一个需要紧密合作的开发团队中,如果能稍加注意礼貌对待他人,将会有

    益于整个团队关注真正有价值的问题,而不是勾心斗角,误入歧途。我们每

    个人都能有一些极好的创新想法,同样也会萌生一些很愚蠢的想法。

    如果你准备提出一个想法,却担心有可能被嘲笑,或者你要提出一个建议,却担心自己丢面子,那么你就不会主动提出自己的建议了。然而,好的软件

    开发作品和好的软件设计,都需要大量的创造力和洞察力。分享并融合各种

    不同的想法和观点,远远胜于单个想法为项目带来的价值。

    消极扼杀创新

    Negativity kills innovation

    负面的评论和态度扼杀了创新。现在,我们并不提倡在设计方案的会议上手

    拉手唱《学习雷锋好榜样》,这样也会降低会议的效率。但是,你必须把重

    点放在解决问题上,而不是去极力证明谁的主意更好。在团队中,一个人只

    是智商高是没有用的,如果他还很顽固并且拒绝合作,那就更糟糕。在这样

    的团队中,生产率和创新都会频临灭亡的边缘。

    我们每个人都会有好的想法,也会有不对的想法,团队中的每个人都需要自

    由地表达观点。即使你的建议不被全盘接受,也能对最终解决问题有所帮

    助。不要害怕受到批评。记住,任何一个专家都是从这里开始的。用Les

    Brown②的一句话说就是:“你不需要很出色才能起步,但是你必须起步才

    能变得很出色。”

    ② 莱斯·布朗,全球领军励志演讲家和作家。——编者注

    团体决策的骆驼

    集体决策确实非常有效,但也有一些最好的创新源于很有见地的个人的独

    立思考。如果你是一个有远见的人,就一定要特别尊重别人的意见。你是一个掌舵者,一定要把握方向,深思熟虑,吸取各方的意见。

    另一个极端是缺乏生气的委员会,每个设计方案都需要全票通过。这样的

    委员会总是小题大作,如果让他们造一匹木马,很可能最后造出的是骆

    驼。

    我们并不是建议你限制会议决策,只是你不应该成为一意孤行的首席架构

    师的傀儡。这里建议你牢记亚里士多德的一句格言:“能容纳自己并不接

    受的想法,表明你的头脑足够有学识。”

    下面是一些有效的特殊技术。

    设定最终期限 。如果你正在参加设计方案讨论会,或者是寻找解决方案时

    遇到问题,请设定一个明确的最终期限,例如午饭时间或者一天的结束。这

    样的时间限制可以防止人们陷入无休止的理论争辩之中,保证团队工作的顺

    利进行。同时(我们觉得)应现实一些:没有最好 的答案,只有更合适的

    方案。设定期限能够帮你在为难的时候果断做出决策,让工作可以继续进

    行。

    逆向思维 。团队中的每个成员都应该意识到权衡的必要性。一种客观对待

    问题的办法是:先是积极地看到它的正面,然后再努力地从反面去认识它

    ③。目的是要找出优点最多缺点最少的那个方案,而这个好办法可以尽可能

    地发现其优缺点。这也有助于少带个人感情。

    ③ 参见“Debating with Knives”,在

    http:blogs.pragprog.comcgi-

    binpragdave.cgiRandomFishBow1.rdoc 。

    设立仲裁人 。在会议的开始,选择一个仲裁人作为本次会议的决策者。每

    个人都要有机会针对问题畅所欲言。仲裁人的责任就是确保每个人都有发言

    的机会,并维持会议的正常进行。仲裁人可以防止明星员工操纵会议,并及

    时打断假大空式发言。

    如果你自己没有积极参与这次讨论活动,那么你最好退一步做会议的监督

    者。仲裁人应该专注于调停,而不是发表自己的观点(理想情况下不应在整

    个项目中有既得利益)。当然,这项任务不需要严格的技术技能,需要的是

    和他人打交道的能力。

    支持已经做出的决定 。一旦方案被确定了(不管是什么样的方案),每个

    团队成员都必须通力合作,努力实现这个方案。每个人都要时刻记住,我们

    的目标是让项目成功满足用户需求。客户并不关心这是谁的主意——他们关

    心的是,这个软件是否可以工作,并且是否符合他们的期望。结果最重要。设计充满了妥协(生活本身也是如此),成功属于意识到这一点的团队。工

    作中不感情用事是需要克制力的,而你若能展现出成熟大度来,大家一定不

    会视而不见。这需要有人带头,身体力行,去感染另一部分人。

    对事不对人 。让我们骄傲的应该是解决了问题,而不是比较出谁的

    主意更好。

    切身感受

    一个团队能够很公正地讨论一些方案的优点和缺点,你不会因为拒绝了有太

    多缺陷的方案而伤害别人,也不会因为采纳了某个不甚完美(但是更好的)

    解决方案而被人忌恨。

    平衡的艺术

    尽力贡献自己的好想法,如果你的想法没有被采纳也无需生气。不要因

    为只是想体现自己的想法而对拟定的好思路画蛇添足。

    脱离实际的反方观点会使争论变味。若对一个想法有成见,你很容易提

    出一堆不太可能发生或不太实际的情形去批驳它。这时,请先扪心自

    问:类似问题以前发生过吗?是否经常发生?

    也就是说,像这样说是不够的:我们不能采用这个方案,因为数据库厂

    商可能会倒闭。或者:用户绝对不会接受那个方案。你必须要评判那些

    场景发生的可能性有多大。想要支持或者反驳一个观点,有时候你必须

    先做一个原型或者调查出它有多少的同意者或者反对者。

    在开始寻找最好的解决方案之前,大家对“最好 ”的含义要先达成共

    识。在开发者眼中的最好,不一定就是用户认为最好的,反之亦然。

    只有更好 ,没有最好 。尽管“最佳实践”这个术语到处在用,但实际

    上不存在“最佳”,只有在某个特定条件下更好的实践。

    不带个人情绪并不是要盲目地接受所有的观点。用合适的词和理由去解

    释为什么你不赞同这个观点或方案,并提出明确的问题。

    4 排除万难,奋勇前进“如果你发现其他人的代码有问题,只要你自己心里知道就可以了。毕

    竟,你不想伤害他们,或者惹来麻烦。如果他是你的老板,更要格外

    谨慎,只要按照他的命令执行就可以了。”

    有一则寓言叫“谁去给猫系铃铛”(Who Will Bell the Cat)。老鼠们

    打算在猫的脖子上系一个铃铛,这样猫巡逻靠近的时候,就能预先得到警

    报。每只老鼠都点头,认为这是一个绝妙的想法。这时一只年老的老鼠问

    道:“那么,谁愿意挺身而出去系铃铛呢?”毫无疑问,没有一只老鼠站出

    来。当然,计划也就这样泡汤了。

    有时,绝妙的计划会因为勇气不足而最终失败。尽管前方很危险——不管是

    真的鱼雷或者只是一个比喻——你必须有勇气向前冲锋,做你认为对的事

    情。

    假如要你修复其他人编写的代码,而代码很难理解也不好使用。你是应该继

    续修复工作,保留这些脏乱的代码呢,还是应该告诉你的老板,这些代码太

    烂了,应该通通扔掉呢?

    也许你会跳起来告诉周围的人,那些代码是多么糟糕,但那只是抱怨和发

    泄,并不能解决问题。相反,你应该重写这些代码,并比较重写前后的优缺

    点。动手证明(不要只是嚷嚷)最有效的方式,是把糟糕的代码放到一边,立刻重写。列出重写的理由,会有助于你的老板(以及同事)认清当前形

    势,帮助他们得到正确的解决方案。

    再假定你在处理一个特定的组件。突然,你发现完全弄错了,你需要推翻重

    来。当然,你也会很担心向团队其他成员说明这个问题,以争取更多的时间

    和帮助。

    当发现问题时,不要试图掩盖这些问题。而要有勇气站起来,说:“我现在

    知道了,我过去使用的方法不对。我想到了一些办法,可以解决这个问题

    ——如果你有更好的想法,我也很乐意听一听——但可能会花多些时间。”

    你已经把所有对问题的负面情绪抛诸脑后,你的意图很清楚,就是寻找解决

    方案。既然你提出大家一起努力来解决问题,那就不会有任何争辩的余地。

    这样会促进大家去解决问题。也许,他们就会主动走近,提供帮助。更重要

    的是,这显示出了你的真诚和勇气,同时你也赢得了他们的信任。

    Venkat如是说……

    践行良好习惯

    我曾经开发过一个应用系统。它向服务器程序发送不同类型的文件,再另

    存为另外一种格式的文件。这应该不难。当我开始工作的时候,我震惊地发现,处理每种类型文件的代码都是重复的。所以,我也配合了一下,复

    制了数百行的代码,改变了其中的两行代码,几分钟之内就让它工作起

    来,但我却感觉很失落。因为我觉得这有悖于良好的工作习惯。

    后来我说服了老板,告诉他代码的维护成本很快就会变得非常高,应该重

    构代码。一周之内,我们重构了代码,并立即由此受益,我们需要修改文

    件的处理方式,这次我们只需要改动一个地方就可以了,而不必遍查整个

    系统。

    你深知怎样做才是正确的,或者至少知道目前的做法是错误的。要有勇气向

    其他的项目成员、老板或者客户解释你的不同观点。当然,这并不容易。也

    许你会拖延项目的进度,冒犯项目经理,甚至惹恼投资人。但你都要不顾一

    切,向着正确的方向奋力前进。

    美国南北战争时的海军上将David Farragut曾经说过一句名言:“别管他妈

    的鱼雷,Drayton上校,全速前进。”确实,前面埋伏着水雷(那时叫鱼

    雷),但是要突破防线,只有全速前进①。

    ① 事实上,Farragut的原话往往被简化为:“别管他妈的鱼雷,全速前

    进!”

    他们做得很对!

    做正确的事 。要诚实,要有勇气去说出实情。有时,这样做很困

    难,所以我们要有足够的勇气。

    切身感受

    勇气会让人觉得有点不自在,提前鼓足勇气更需要魄力。但有些时候,它是

    扫除障碍的唯一途径,否则问题就会进一步恶化下去。鼓起你的勇气,这能

    让你从恐惧中解脱出来。

    平衡的艺术

    如果你说天快要塌下来了,但其他团队成员都不赞同。反思一下,也许

    你是正确的,但你没有解释清楚自己的理由。

    如果你说天快要塌下来了,但其他团队成员都不赞同。认真考虑一下,他们也许是对的。如果设计或代码中出现了奇怪的问题,花时间去理解为什么代码会是这

    样的。如果你找到了解决办法,但代码仍然令人费解,唯一的解决办法

    是重构代码,让它可读性更强。如果你没有马上理解那段代码,不要轻

    易地否定和重写它们。那不是勇气,而是鲁莽。

    当你勇敢地站出来时,如果受到了缺乏背景知识的抉择者的抵制,你需

    要用他们能够听懂的话语表达。“更清晰的代码”是无法打动生意人

    的。节约资金、获得更好的投资回报,避免诉讼以及增加用户利益,会

    让论点更有说服力。

    如果你在压力下要对代码质量作出妥协,你可以指出,作为一名开发

    者,你没有职权毁坏公司的资产(所有的代码)。

    第3章 学无止境

    即使你已经在正确的轨道上,但如果只是停止不前,也仍然会被淘汰

    出局。

    ——Will Rogers(美国著名演员)

    敏捷需要持续不断的学习和充电。正如上面引用的Will Rogers的话,逆水

    行舟,不进则退。那不仅是赛马场上的真理,它更适合我们当今的程序员。

    软件开发行业是一个不停发展和永远变化的领域。虽然有一些概念一直有

    用,但还有很多知识很快就会过时。从事软件开发行业就像是在跑步机上,你必须一直跟上步伐稳步前进,否则就会摔倒出局。

    谁会帮助你保持步伐前进呢?在一个企业化的社会中,只有一个人会为你负

    责——你自己。是否能跟上变化,完全取决于你自己。

    许多新技术都基于现有的技术和思想。它们会加入一些新的东西,这些新东

    西是逐步加入的量。如果你跟踪技术变化,那么学习这些新东西对你来说就

    是了解这些增量变化。如果你不跟踪变化,技术变化就会显得很突然并且难

    以应付。这就好比少小离家老大回,你会发现变化很大,甚至有很多地方都

    不认识了。然而,居住在那里的人们,每天只看到小小的变化,所以非常适

    应。在第28页我们会介绍一些跟踪变化 的方法。给自己投资,让自己与时俱进,当然再好不过,但是也要努力对团队投资

    ,这个目标怎么实现呢?你将从第31页学到实现这个目标的一些方法。

    学习新的技术和新的开发方法很重要,同时你也要能摒弃陈旧和过时的开发

    方法。换句话说,你需要懂得丢弃 (请阅读第34页)。

    当我们谈到变化这个话题的时候,要认识到你对问题的理解在整个项目期间

    也是在变化的。你曾经认为自己已经很明白的事情,现在也许并不是你想象

    中那样。你要对没有完全理解的某些疑问不懈地深入追踪下去,我们将从第

    37页开始讲述为什么要打破砂锅问到底 ,以及如何有效地提问。

    最后,一个活力十足的敏捷开发团队需要有规律反复地做很多事情,一旦项

    目开始运作,你就要把握开发节奏 ,我们会在第40页介绍这种节奏感。

    5 跟踪变化

    “软件技术的变化如此之快,势不可挡,这是它的本性。继续用你熟

    悉的语言做你的老本行吧,你不可能跟上技术变化的脚步。”

    赫拉克利特说过:“唯有变化是永恒的。”历史已经证明了这句真理,在当

    今快速发展的IT时代尤其如此。你从事的是一项充满激情且不停变化的工

    作。如果你毕业于计算机相关的专业,并觉得自己已经学完了所有知识,那

    你就大错特错了。

    假设你是10多年前的1995年毕业的,那时,你掌握了哪些技术呢?可能你的

    C++还学得不错,你了解有一门新的语言叫Java,一种被称作是设计模式的

    思想开始引起大家的关注。一些人会谈论被称作因特网的东东。如果那个时

    候你就不再学习,而在2005年的时候重出江湖。再看看周围,就会发现变化

    巨大。就算是在一个相当狭小的技术领域,要学习那些新技术并达到熟练的

    程度,一年的时间也不够。

    技术发展的步伐如此快速,简直让人们难以置信。就以Java为例,你掌握了

    Java语言及其一系列的最新特性。接着,你要掌握Swing、Servlet、JSP 、Struts、Tapestry、JSF、JDBC、JDO、Hibernate、JMS、EJB、Lucene、Spring……还可以列举很多。如果你使用的是微软的技术,要掌握VB、Visual C++、MFC、COM、ATL、.NET、C、VB.NET、ASP.NET、ADO.NET、WinForm、Enterprise Service、Biztalk……并且,不要忘记还有UML、Ruby、XML、DOM、SAX、JAXP、JDOM、XSL、Schema、SOAP、Web Service、SOA,同样还可以继续列举下去(我们将会用光所有的缩写字母)。不幸的是,如果只是掌握了工作中需要的技术并不够。那样的工作也许几年

    之后就不再有了——它会被外包或者会过时,那么你也将会出局①。

    ① 参考My Job Went to India: 52 Ways to Save Your Job [Fow05]一

    书。新版改名为Passionate Programmer 。

    假设你是Visual C++或者VB程序员,看到COM技术出现了。你花时间去学习

    它(虽然很痛苦),并且随时了解分布式对象计算的一切。当XML出现的时

    候,你花时间学习它。你深入研究ASP,熟知如何用它来开发Web应用。你虽

    然不是这些技术的专家,但也不是对它们一无所知。好奇心促使你去了解

    MVC是什么,设计模式是什么。你会使用一点Java,去试试那些让人兴奋的

    功能。

    如果你跟上了这些新技术,接下来学习.NET技术就不再是大问题。你不需要

    一口气爬上10楼,而需要一直在攀登,所以最后看起来就像只要再上一二

    层。如果你对所有这些技术都一无所知,想要马上登上这10楼,肯定会让你

    喘不过气来。而且,这也会花很长时间,期间还会有更新的技术出现。

    如何才能跟上技术变化的步伐呢?幸好,现今有很多方法和工具可以帮助我

    们继续充电。下面是一些建议。

    迭代和增量式的学习 。每天计划用一段时间来学习新技术,它不需要很长

    时间,但需要经常进行。记下那些你想学习的东西——当你听到一些不熟悉

    的术语或者短语时,简要地把它记录下来。然后在计划的时间中深入研究

    它。

    了解最新行情 。互联网上有大量关于学习新技术的资源。阅读社区讨论和

    邮件列表,可以了解其他人遇到的问题,以及他们发现的很酷的解决方案。

    选择一些公认的优秀技术博客,经常去读一读,以了解那些顶尖的博客作者

    们正在关注什么(最新的博客列表请参考pragmaticprogrammer.com)。

    参加本地的用户组活动 。Java、Ruby、Delphi、.NET、过程改进、面向对

    象设计、Linux、Mac,以及其他的各种技术在很多地区都会有用户组。听讲

    座,然后积极加入到问答环节中。

    参加研讨会议 。计算机大会在世界各地举行,许多知名的顾问或作者主持

    研讨会或者课程。这些聚会是向专家学习的最直接的好机会。

    如饥似渴地阅读 。找一些关于软件开发和非技术主题的好书(我们很乐意

    为你推荐),也可以是一些专业的期刊和商业杂志,甚至是一些大众媒体新

    闻(有趣的是在那里常常能看到老技术被吹捧为最新潮流)。 跟踪技术变化 。你不需要精通所有技术,但需要清楚知道行业的动

    向,从而规划你的项目和职业生涯。

    切身感受

    你能嗅到将要流行的新技术,知道它们已经发布或投入使用。如果必须要把

    工作切换到一种新的技术领域,你能做到。

    平衡的艺术

    许多新想法从未变得羽翼丰满,成为有用的技术。即使是大型、热门和

    资金充裕的项目也会有同样的下场。你要正确把握自己投入的精力。

    你不可能精通每一项技术,没有必要去做这样的尝试。只要你在某些方

    面成为专家,就能使用同样的方法,很容易地成为新领域的专家。

    你要明白为什么需要这项新技术——它试图解决什么样的问题?它可以

    被用在什么地方?

    避免在一时冲动的情况下,只是因为想学习而将应用切换到新的技术、框架或开发语言。在做决策之前,你必须评估新技术的优势。开发一个

    小的原型系统,是对付技术狂热者的一剂良药。

    6 对团队投资

    “不要和别人分享你的知识——自己留着。你是因为这些知识而成为

    团队中的佼佼者,只要自己聪明就可以了,不用管其他失败者。”

    团队中的开发者们各有不同的能力、经验和技术。每个人都各有所长。不同

    才能和背景的人混在一起,是一个非常理想的学习环境。

    在一个团队中,如果只是你个人技术很好还远远不够。如果其他团队成员的

    知识不够,团队也无法发挥其应有的作用:一个学习型的团队才是较好的团

    队。

    当开发项目的时候,你需要使用一些术语或者隐喻来清晰地传达设计的概念

    和意图。如果团队中的大部分成员不熟悉这些,就很难进行高效地工作。再

    比如你参加了一个课程或者研讨班之后,所学的知识如果不用,往往就会忘记。所以,你需要和其他团队成员分享所学的知识,把这些知识引入团队

    中。

    找出你或团队中的高手擅长的领域,帮助其他的团队成员在这些方面迎头赶

    上(这样做还有一个好处是,可以讨论如何将这些东西应用于自己的项目

    中)。

    “午餐会议”是在团队中分享知识非常好的方式。在一周之中挑选一天,例

    如星期三(一般来说任何一天都可以,但最好不要是星期一和星期五)。事

    先计划午餐时聚集在一起,这样就不会担心和其他会议冲突,也不需要特别

    的申请。为了降低成本,就让大家自带午餐。

    每周,要求团队中的一个人主持讲座。他会给大家介绍一些概念,演示工

    具,或者做团队感兴趣的任何一件事情。你可以挑一本书,给大家说说其中

    一些特别内容、项目或者实践。①无论什么主题都可以。

    ① Pragmatic公司的出版人Andy和Dave曾听不少人说,他们成立了读书小

    组,讨论和研究Pragmatic公司的图书。

    每个人都比你厉害吗?嗯,那太好了!

    享有盛名的爵士吉他手Pat Methany说过这样一句话:“总是要成为你所

    在的那个乐队中最差的乐手。如果你是乐队中最好的乐手,就需要重新选

    择乐队了。我认为这也适用于乐队之外的其他事情。”

    为什么是这样呢?如果你是团队中最好的队员,就没有动力继续提高自

    己。如果周围的人都比你厉害,你就会有很强的动力去追赶他们。你将会

    在这样的游戏中走向自己的顶峰。

    从每周主持讲座的人开始,先让他讲15分钟,然后,进行开放式讨论,这样

    每个人都可以发表自己的意见,讨论这个主题对于项目的意义。讨论应该包

    括所能带来的益处,提供来自自己应用程序的示例,并准备好听取进一步的

    信息。

    这些午餐会议非常有用。它促进了整个团队对这个行业的了解,你自己也可

    以从其他人身上学到很多东西。优秀的管理者会重用那些能提高其他团队成

    员价值的人,因此这些活动也直接有助于你的职业生涯。 提供你和团队学习的更好平台 。通过午餐会议可以增进每个人的知

    识和技能,并帮助大家聚集在一起进行沟通交流。唤起人们对技术和技巧

    的激情,将会对项目大有裨益。

    切身感受

    这样做,会让每个人都觉得自己越来越聪明。整个团队都要了解新技术,并

    指出如何使用它,或者指出需要注意的缺陷。

    平衡的艺术

    读书小组逐章一起阅读一本书,会非常有用,但是要选好书。《7天用

    设计模式和UML精通……》也许不会是一本好书。

    不是所有的讲座都能引人入胜,有些甚至显得不合时宜。不管怎么样,都要未雨绸缪;诺亚在建造方舟的时候,可并没有开始下雨,谁能料到

    后来洪水泛滥呢?

    尽量让讲座走入团队中。如果午餐会议在礼堂中进行,有餐饮公司供

    饭,还要使用幻灯片,那么就会减少大家接触和讨论的机会。

    坚持有计划有规律地举行讲座。持续、小步前进才是敏捷。稀少、间隔

    时间长的马拉松式会议非敏捷也。

    如果一些团队成员因为吃午饭而缺席,用美食引诱他们。

    不要局限于纯技术的图书和主题,相关的非技术主题(项目估算、沟通

    技巧等)也会对团队有帮助。

    午餐会议不是设计会议。总之,你应专注讨论那些与应用相关的一般主

    题。具体的设计问题,最好是留到设计会议中去解决。

    7 懂得丢弃

    “那就是你一贯的工作方法,并且是有原因的。这个方法也很好地为

    你所用。开始你就掌握了这个方法,很明显它是最好的方法。真的,从那以后就不要再改变了。”敏捷的根本之一就是拥抱变化。既然变化是永恒的,你有可能一直使用相同

    的技术和工具吗?

    不,不可能。我们一直在本章说要学习新技术和新方法。但是记住,你也需

    要学会如何丢弃。

    随着科技进步,曾经非常有用的东西往往会靠边站。它们不再有用了,它们

    还会降低你的效率。当Andy第一次编程的时候,内存占用是一个大问题。你

    通常无法在主存储器(大约48KB)中一次装载整个程序,所以必须把程序切

    分成块。当一个程序块交换进去的时候,其他一些程序块必须出来,并且你

    无法在一个块中调用另一个块中的函数。

    正是这种实际约束,极大地影响了你的设计和编程技术。

    想想在过去,面对处理器之外的循环操作,你必须花费很大精力去手工调整

    汇编语言的编译输出。可以想象,如果是使用JavaScript或者J2EE代码,你

    还需要这么干吗?

    对于大多数的商业应用,技术已经有了巨大的变化,不再像过去那样,处处

    考虑内存占用、手动的重复占位及手工调整汇编语言。① 但我们仍然看到

    很多开发者从未丢弃这些旧习惯。

    ① 这些技术现在仍然用于嵌入式系统领域的开发。

    Andy曾经看到过这样一段C语言代码:一个大的for 循环,循环里面的代码

    一共输出了60页。那个作者“不相信”编译器的优化,所以决定自己手工实

    现循环体展开和其他一些技巧。我们只能祝愿维护那一大堆代码的人好运。

    在过去,这段代码也许可以勉强接受。但是,现在绝对不可以了。电脑和

    CPU曾经非常昂贵,而现在它们就是日用品。现在,开发者的时间才是紧缺

    和昂贵的资源。

    这样的转变在缓慢地进行着,但是人们也真正认清了这个事实。我们看到,需要耗费10人年开发的J2EE项目已经从辉煌走向下坡路。使用PHP,一个月

    的时间就可以完成,并能交付大部分的功能。像PHP这样的语言和Ruby on

    Rails这样的框架越来越受到关注(参见[TH05]),这表明了开发者已经意

    识到旧的技术再也行不通了。

    但丢弃已经会的东西并不容易。很多团队在犹豫,是因为管理者拒绝用500

    美元购买一台构建机器(build machine),却宁愿花费好几万美元的人工

    费,让程序员花时间找出问题。而实际上,买一台构建机器就可以解决这些问题。如果购买机器需要花费500 000美元,那样做还情有可原,但现在早

    已时过境迁了。

    根深蒂固的习惯不可能轻易地就丢弃掉

    Expensive mental models aren’t discarded lightly

    在学习一门新技术的时候,多问问自己,是否把太多旧的态度和方法用在了

    新技术上。学习面向对象编程和学习面向过程编程是截然不同的。很容易会

    发现有人用C语言的方式编写Java代码,用VB的方式编写C的代码(或者用

    Fortran的方式做任何事情)。这样,你辛苦地转向一门新的语言,却失去

    了期望获得的益处。

    打破旧习惯很难,更难的是自己还没有意识到这个问题。丢弃的第一步,就

    是要意识到 你还在使用过时的方法,这也是最难的部分。另一个难点就是

    要做到真正地丢弃旧习惯。思维定式是经过多年摸爬滚打才构建成型的,已

    经根深蒂固,没有人可以很容易就丢弃它们。

    这也不是说你真地要完全丢弃它们。前面那个内存重复占位的例子,只是在

    稍大缓存中用手工维护一组工件的特殊案例。尽管实现方式不同了,但以前

    的技术还在你的大脑中。你不可能撬开大脑,把这一段记忆神经剪掉。其

    实,根据具体情况还可以运用旧知识。如果环境合适,可以举一反三地灵活

    应用,但一定要保证不是习惯性地落入旧习惯。

    应该力求尽可能完全转入新的开发环境。例如,学习一门新的编程语言时,应使用推荐的集成开发环境,而不是你过去开发时用的工具插件。用这个工

    具编写一个和过去完全不同类型的项目。转换的时候,完全不要使用过去的

    语言开发工具。只有更少被旧习惯牵绊,才更容易养成新习惯。

    学习新的东西,丢弃旧的东西 。在学习一门新技术的时候,要丢弃

    会阻止你前进的旧习惯。毕竟,汽车要比马车车厢强得多。

    切身感受

    新技术会让人感到有一点恐惧。你确实需要学习很多东西。已有的技能和习

    惯为你打下了很好的基础,但不能依赖它们。

    平衡的艺术

    沉舟侧畔千帆过,病树前头万木春。要果断丢弃旧习惯,一味遵循过时

    的旧习惯会危害你的职业生涯。不是完全忘记旧的习惯,而是只在使用适当的技术时才使用它。

    对于所使用的语言,要总结熟悉的语言特性,并且比较这些特性在新语

    言或新版本中有什么不同。

    8 打破砂锅问到底

    “接受别人给你的解释。别人告诉你问题出在了什么地方,你就去看

    什么地方。不需要再浪费时间去追根究底。”

    前面谈到的一些习惯是关于如何提高你和团队的技术的。下面有一个习惯几

    乎总是有用,可以用于设计、调试以及理解需求。

    假设,应用系统出了大问题,他们找你来修复它。但你不熟悉这个应用系

    统,所以他们会帮助你,告诉你问题一定是出在哪个特殊的模块中——你可

    以放心地忽略应用系统的其他地方。你必须很快地解决这个问题,因为跟你

    合作的这些人耐心也很有限。

    当你受到那些压力的时候,也许会觉得受到了胁迫,不想去深入了解问题,而且别人告诉你的已经够深入了。然而,为了解决问题,你需要很好地了解

    系统的全局。你需要查看所有你认为和问题相关的部分——即便其他人觉得

    这并不相干。

    观察一下医生是如何工作的。当你不舒服的时候,医生会问你各种各样的问

    题——你有什么习惯,你吃了什么东西,什么地方疼痛,你已经服过什么样

    的药等。人的身体非常复杂,会受到很多因素的影响。如果医生没有全面地

    了解状况,就很可能出现误诊。

    例如,住在纽约市的一个病人患有高烧、皮疹、严重的头痛、眼睛后面疼

    痛,以及肌肉和关节疼痛,他也许是染上了流感或者麻疹。但是,通过全面

    的检查,医生发现这个倒霉的病人刚去南美洲度假回来。所以,这病也许并

    不是简单的流感,还有可能是在新大陆染上的热带传染病登革热。

    在计算机世界中也很相似,很多问题都会影响你的应用系统。为了解决问

    题,你需要知道许多可能的影响因素。当找人询问任何相关的问题时,让他

    们耐心地回答你的问题,这是你的职责。

    或者,假设你和资深的开发者一起工作。他们可能比你更了解这个系统。但

    他们也是人,有时他们也会忘记一些东西。你的问题甚至会帮助他们理清思路。你从一个新人角度提出的问题,给他们提供了一个新的视角,也许就帮

    助他们解决了一直令人困扰的问题。

    “为什么”是一个非常好的问题。事实上,在一本流行的管理图书《第五项

    修炼》中,作者建议,在理解一个问题的时候,需要渐次地问5个以上的

    “为什么”。这听起来就像退回到了4岁,那时对一切都充满着好奇。它是

    很好的方式,进一步挖掘简单直白的答案,通过这个路线,设想就会更加接

    近事实真相。

    在《第五项修炼》一书中就有这样的例子。咨询师访问一个制造设备工厂的

    经理,就用到了这样一些追根究底的分析。看到地板上有油渍的时候,经理

    的第一反应是命令工人把它打扫干净。但是,咨询师问:“为什么地板上会

    有油渍?”经理不熟悉整个流程,就会责备这是清洁队的疏忽。咨询师再次

    问道:“为什么地板上有油渍?”通过一系列渐次提出的“为什么”和许多

    不同部门员工的帮助,咨询师最后找到了真正的问题所在:采购政策表述不

    明确,导致大量采购了一批有缺陷的垫圈。

    答案出来之后,经理和其他员工都十分震惊,他们对这事一无所知。由此发

    现了一个重大的隐患,避免了其他方面更大的损失。而咨询师所做的不过就

    是问了“为什么”。

    “哎呀,只要每周重启一次系统,就没有问题了。”真的吗?为什么呀?

    “你必须依次执行3次构建才能完成构建。”真的吗?为什么呀?“我们的

    用户根本不想要那个功能。”真的吗?为什么呀?

    为什么呀?

    不停地问为什么 。不能只满足于别人告诉你的表面现象。要不停地

    提问直到你明白问题的根源。

    切身感受

    这就好比是从矿石中采掘贵重的珠宝。你不停地筛选掉无关的物质,一次比

    一次深入,直到找到发光的宝石。你要能感觉到真正地理解了问题,而不是

    只知道表面的症状。

    平衡的艺术

    你可能会跑题,问了一些与主题无关的问题。就好比是,如果汽车启动

    不了,你问是不是轮胎出了问题,这是没有任何帮助的。问“为什么”,但是要问到点子上。

    当你问“为什么”的时候,也许你会被反问:“为什么你问这个问

    题?”在提问之前,想好你提问的理由,这会有助于你问出恰当的问

    题。

    “这个,我不知道”是一个好的起点,应该由此进行更进一步的调查,而不应在此戛然结束。

    9 把握开发节奏

    “我们很长时间没有进行代码复审,所以这周会复审所有的代码。此

    外,我们也要做一个发布计划了,那就从星期二开始,用3周时间,做

    下一个发布计划。”

    在许多不成功的项目中,基本上都是随意安排工作计划,没有任何的规律。

    那样的随机安排很难处理。你根本不知道明天将会发生什么,也不知道什么

    时候开始下一轮的全体“消防演习”。

    但是,敏捷项目会有一个节奏和循环,让开发更加轻松。例如,Scrum约定

    了30天之内不应发生需求变化,这样确保团队有一个良性的开发节奏。这有

    助于防止一次计划太多的工作和一些过大的需求变更。

    相反,很多敏捷实践必须一直进行,也就是说,它贯穿于项目的整个生命周

    期。有人说,上帝发明了时间,就是为了防止所有事情同时发生。因此我们

    需要更具远见,保持不同的开发节奏,这样敏捷项目的所有事情就不会突然

    同时发生,也不会随机发生,时间也不会不可预知。

    我们先来看某个工作日的情况。你希望每天工作结束的时候,都能完成自己

    的工作,你手上没有遗留下任何重要的任务。当然,每天都能这样是不现实

    的。但是,你可以做到在每天下班离开公司前运行测试,并提交一天完成的

    代码。如果已经很晚了,并且你只是尝试性地编写了一些代码,那么也许最

    好应该删掉这些代码,第二天从头开始。

    这个建议听起来十分极端,也许确实有一点。①但是如果你正在开发小块的

    任务,这种方式非常有助于你管理自己的时间:如果在你工作的时候没有一

    个固定的最终期限(例如一天的结束),就应该好好想想了。它会让你的工

    作有一个节奏,在每天下班的时候,提交所有的工作,开心地收工。这样,明天就能开始新的内容,解决下一系列难题。① Ron Jeffrey告诉我们:“我希望人们敢于经常这么做。”

    时 间 盒

    敏捷开发者可以从多方面得到反馈:用户、团队成员和测试代码。这些反

    馈会帮助你驾驭项目。但是时间本身就是一个非常重要的反馈。

    许多的敏捷技巧来源于时间盒——设定一个短时的期限,为任务设定不能

    延长的最终期限。你可以选择放弃其他方面的任务,但是最终期限是不变

    的。你可能不知道完成所有的任务需要多少个时间盒,但每个时间盒必须

    是短期的、有限的,并且要完成具体的目标。

    例如,迭代一般是两周的时间。当时间到的时候,迭代就完成了。那部分

    是固定不变的,但是在一个具体的迭代中完成哪些功能是灵活的。换句话

    说,你不会改变时间,但是你可以改变功能。相似地,你会为设计讨论会

    设定一个时间盒,即到了指定的时间点,会议就结束,同时必须要做出最

    终的设计决策。

    当你遇到艰难抉择的时候,固定的时间期限会促使你做决定。你不能在讨

    论或功能上浪费很多时间,这些时间可以用于具体的工作。时间盒会帮助

    你一直前进。

    鲨鱼必须不停地向前游,否则就会死亡。在这方面,软件项目就像是鲨

    鱼,你需要不停地前进,同时要清楚自己的真实进度。

    站立会议(习惯38,第148页)最好每天在固定的时间和地点举行,比如说

    上午10点左右。要养成这样的习惯,在那时就准备好一切参加站立会议。

    最大的节拍就是迭代时间(习惯17,第69页),一般是1~4周的时间。不管

    你的一个迭代是多长,都应该坚持——确保每个迭代周期的时间相同很重

    要。运用有规律的开发节奏,会更容易达到目标,并确保项目不停地前进。

    解决任务,在事情变得一团糟之前 。保持事件之间稳定重复的间

    隔,更容易解决常见的重复任务。

    切身感受

    项目开发需要有一致和稳定的节奏。编辑,运行测试,代码复审,一致的迭

    代,然后发布。如果知道什么时候开始下一个节拍,跳舞就会更加容易。平衡的艺术

    在每天结束的时候,测试代码,提交代码,没有残留的代码。

    不要搞得经常加班。

    以固定、有规律的长度运行迭代(第69页,习惯17)。也许刚开始你要

    调整迭代的长度,找到团队最舒服可行的时间值,但之后就必须要坚

    持。

    如果开发节奏过于密集,你会精疲力竭的。一般来说,当与其他团队

    (或组织)合作时,你需要减慢开发节奏。因此人们常说,互联网时代

    发展太快,有害健康。

    有规律的开发节奏会暴露很多问题,让你有更多鼓起勇气的借口(第23

    页,习惯4)。

    就像是减肥一样,一点点的成功也是一个很大的激励。小而可达到的目

    标会让每个人全速前进。庆祝每一次难忘的成功:共享美食和啤酒或者

    团队聚餐。

    第4章 交付用户想要的软件

    没有任何计划在遇敌后还能继续执行。

    ——Helmuth von Moltke(德国陆军元帅,1848—1916)

    客户把需求交给你了,要你几年后交付这个系统。然后,你就基于这些需求

    构建客户需要的系统,最后按时交付。客户看到了软件,连声称赞做得好。

    从此你又多了一个忠实客户,接着你很开心地进入了下一个项目。你的项目

    通常都是这样运作的,是这样的吗?

    其实,大部分人并不会遇到这样的项目。通常情况是:客户最后看到了软

    件,要么震惊要么不高兴。他们不喜欢所看到的软件,他们认为很多地方需

    要修改。他们要的功能不在他们给你的原始需求文档中。这听起来是不是更

    具代表性?Helmuth von Moltke曾说过:“没有任何计划在遇敌后还能继续执行。”我

    们的敌人不是客户,不是用户,不是队友,也不是管理者。真正的敌人是变

    化。软件开发如战争,形势的变化快速而又剧烈。固守昨天的计划而无视环

    境的变化会带来灾难。你不可能“战胜”变化——无论它是设计、架构还是

    你对需求的理解。敏捷——成功的软件开发方法——取决于你识别和适应变

    化的能力。只有这样才有可能在预算之内及时完成开发,创建真正符合用户

    需求的系统。

    在本章中,我们会介绍如何达到敏捷的目标。首先,要介绍为什么用户和客

    户参与开发如此重要,以及为什么让客户做决定 (从第45页开始)。设计

    是软件开发的基础,没有它很难做好开发,但你也不能被它牵制。从第48页

    开始,将介绍如何让设计指导而不是操纵开发 。说到牵制,你应确保在项

    目中引入合适的技术。你需要合理地使用技术 (第52页介绍)。

    为了让软件符合用户的需求,要一直做下面的准备工作。为了降低集成新代

    码带来的破坏性变化,你要提早集成,频繁集成 (第58页)。当然,你不

    想破坏已有的代码,想让代码一直保持可以发布 (从第55页开始)。

    你不能一次又一次为用户演示新功能,而浪费宝贵的开发时间,因此你需要

    提早实现自动化部署 (第61页)。只要你的代码一直可用,并且易于向用

    户部署,你就能使用演示获得频繁反馈 (第64页)。这样你就能经常向全

    世界发布新版本。你想通过使用短迭代,增量发布 来帮助经常发布新功

    能,与用户的需求变化联系更紧密(从第69页开始介绍它)。

    最后,特别是客户要求预先签订固定价格合约时,很难通过敏捷的方法让客

    户与我们同坐一条船上。而且,事实上是固定的价格就意味着背叛承诺 ,我们会在第73页了解如何处理这种情况。

    10 让客户做决定

    “开发者兼具创新和智慧,最了解应用程序。因此,所有关键决定都

    应该由开发者定夺。每次业务人员介入的时候,都会弄得一团糟,他

    们无法理解我们做事的逻辑。”

    在设计方面,做决定的时候必须有开发者参与。可是,在一个项目中,他们

    不应该做所有的决定,特别是业务方面的决定。

    就拿项目经理Pat的例子来说吧。Pat的项目是远程开发,一切按计划且在预

    算内进行着——就像是个可以写入教科书的明星项目。Pat高高兴兴地把代

    码带到客户那里,给客户演示,却败兴而归。原来,Pat的业务分析师没有和用户讨论,而是自作主张,决定了所有的问

    题。在整个开发过程中,企业主根本没有参与低级别的决策。项目离完成还

    早着呢,就已经不能满足用户的需要了。这个项目一定会延期,又成为一个

    经典的失败案例。

    因而,你只有一个选择:要么现在就让用户做决定,要么现在就开始开发,迟些让用户决定,不过要付出较高的成本。如果你在开发阶段回避这些问

    题,就增加了风险,但是你要能越早解决这些问题,就越有可能避免繁重的

    重新设计和编码。甚至在接近项目最终期限的时候,也能避免与日俱增的时

    间压力。

    例如,假设你要完成一个任务,有两种实现方式。第一种方式的实现比较

    快,但是对用户有一点限制。第二种方式实现起来需要更多的时间,但是可

    以提供更大的灵活性。很显然,你有时间的压力(什么项目没有时间压力

    呢),那么你就用第一种很快的方式吗?你凭什么做出这样的决定呢?是投

    硬币吗?你询问了同事或者你的项目经理吗?

    作者之一Venkat最近的一个项目就遇到了类似的问题。项目经理为了节约时

    间,采取了第一种方式。也许你会猜到,在Beta版测试的时候,软件暴露出

    的局限让用户震惊,甚至愤怒。结果还得重做,花费了团队更多的金钱、时

    间和精力。

    决定什么不该决定

    Decide what you shouldn’t decide

    开发者(及项目经理)能做的一个最重要的决定就是:判断哪些是自己决定

    不了的,应该让企业主做决定。你不需要自己给业务上的关键问题做决定。

    毕竟,那不是你的事情。如果遇到了一个问题,会影响到系统的行为或者如

    何使用系统,把这个问题告诉业务负责人。如果项目领导或经理试图全权负

    责这些问题,要委婉地劝说他们,这些问题最好还是和真正的业务负责人或

    者客户商议(见习惯4,第23页)。

    当你和客户讨论问题的时候,准备好几种可选择的方案。不是从技术的角

    度,而是从业务的角度,介绍每种方案的优缺点,以及潜在的成本和利益。

    和他们讨论每个选择对时间和预算的影响,以及如何权衡。无论他们做出了

    什么决定,他们必须接受它,所以最好让他们了解一切之后再做这些决定。

    如果事后他们又想要其他的东西,可以公正地就成本和时间重新谈判。

    毕竟,这是他们的决定。 让你的客户做决定 。开发者、经理或者业务分析师不应该做业务方

    面的决定。用业务负责人能够理解的语言,向他们详细解释遇到的问题,并让他们做决定。

    切身感受

    业务应用需要开发者和业务负责人互相配合来开发。这种配合的感觉就应该

    像一种良好的、诚实的工作关系。

    平衡的艺术

    记录客户做出的决定,并注明原因。好记性不如烂笔头。可以使用工程

    师的工作日记或日志、Wiki、邮件记录或者问题跟踪数据库。但是也要

    注意,你选择的记录方法不能太笨重或者太繁琐。

    不要用低级别和没有价值的问题打扰繁忙的业务人员。如果问题对他们

    的业务没有影响,就应该是没有价值的。

    不要随意假设低级别的问题不会影响他们的业务。如果能影响他们的业

    务,就是有价值的问题。

    如果业务负责人回答“我不知道”,这也是一个称心如意的答案。也许

    是他们还没有想到那么远,也许是他们只有看到运行的实物才能评估出

    结果。尽你所能为他们提供建议,实现代码的时候也要考虑可能出现的

    变化。

    11 让设计指导而不是操纵开发

    “设计文档应该尽可能详细,这样,低级的代码工人只要敲入代码就

    可以了。在高层方面,详细描述对象的关联关系;在低层方面,详细

    描述对象之间的交互。其中一定要包括方法的实现信息和参数的注

    释。也不要忘记给出类里面的所有字段。编写代码的时候,无论你发现了

    什么,绝不能偏离了设计文档。”

    “设计”是软件开发过程不可缺少的步骤。它帮助你理解系统的细节,理解

    部件和子系统之间的关系,并且指导你的实现。一些成熟的方法论很强调设

    计,例如,统一过程(Unified Process,UP)十分重视和产品相关的文

    档。项目管理者和企业主常常为开发细节困扰,他们希望在开始编码之前,先有完整的设计和文档。毕竟,那也是你如何管理桥梁或建筑项目的,难道

    不是吗?

    另一方面,敏捷方法建议你早在开发初期就开始编码。是否那就意味着没有

    设计呢?① 不,绝对不是,好的设计仍然十分重要。画关键工作图(例

    如,用UML)是必不可少的,因为要使用类及其交互关系来描绘系统是如何

    组织的。在做设计的时候,你需要花时间去思考(讨论)各种不同选择的缺

    陷和益处,以及如何做权衡。

    ① 查阅Martin Fowler的文章Is Design Dead?

    (http:www.martinfowler.comarticlesdesignDead.html ),它是

    对本主题深入讨论的一篇好文章。

    然后,下一步才考虑是否需要开始编码。如果你在前期没有考虑清楚这些问

    题,就草草地开始编码,很可能会被很多意料之外的问题搞晕。甚至在建筑

    工程方面也有类似的情况。在锯一根木头的时候,通常的做法就是先锯一块

    比需要稍微长一点的木块,最后细致地修整,直到它正好符合需求。

    但是,即使之前已经提交了设计文档,也还会有一些意料之外的情况出现。

    时刻谨记,此阶段提出的设计只是基于你目前 对需求的理解而已。一旦开

    始了编码,一切都会改变。设计及其代码实现会不停地发展和变化。

    一些项目领导和经理认为设计应该尽可能地详细,这样就可以简单地交付给

    “代码工人们”。他们认为代码工人不需要做任何决定,只要简单地把设计

    转化成代码就可以了。就作者本人而言,没有一个愿意在这样的团队中做纯

    粹的打字员。我们猜想你也不愿意。

    设计满足实现即可,不必过于详细

    Design should be only as detailed as needed to implement

    如果设计师们把自己的想法绘制成精美的文档,然后把它们扔给程序员去编

    码,那会发生什么(查阅习惯39,在第152页)?程序员会在压力下,完全

    按照设计或者图画的样子编码。如果系统和已有代码的现状表明接收到的设

    计不够理想,那该怎么办?太糟糕了!时间已经花费在设计上,没有工夫回

    头重新设计了。团队会死撑下去,用代码实现了明明知道是错误的设计。这

    听起来是不是很愚蠢?是够愚蠢的,但是有一些公司真的就是这样做的。

    严格的需求—设计—代码—测试开发流程源于理想化的瀑布式 ②开发方

    法,它导致在前面进行了过度的设计。这样在项目的生命周期中,更新和维

    护这些详细的设计文档变成了主要工作,需要时间和资源方面的巨大投资,却只有很少的回报。我们本可以做得更好。② 瀑布式开发方法意味着要遵循一系列有序的开发步骤,前面是定义详

    细的需求,然后是详细的设计,接着是实现,再接着是集成,最后是测试

    (此时你需要向天祈祷)。那不是作者首先推荐的做法。更多详情可以查

    阅[Roy70]。

    设计可以分为两层:战略 和战术 。前期的设计属于战略,通常只有在没有

    深入理解需求的时候需要这样的设计。更确切地说,它应该只描述总体战

    略,不应深入到具体的细节。

    做到精确

    如果你自己都不清楚所谈论的东西,就根本不可能精确地描述它。

    ——约翰·冯·诺依曼

    前面刚说过,战略级别的设计不应该具体说明程序方法、参数、字段和对象

    交互精确顺序的细节。那应该留到战术设计阶段,它应该在项目开发的时候

    再具体展开。

    战略设计与战术设计

    Strategic versus tactical design

    良好的战略设计应该扮演地图的角色,指引你向正确的方向前进。任何设计

    仅是一个起跑点:它就像你的代码一样,在项目的生命周期中,会不停地进

    一步发展和提炼。

    下面的故事会给我们一些启发。在1804年,Lewis与Clark③进行了横穿美国

    的壮举,他们的“设计”就是穿越蛮荒。但是,他们不知道在穿越殖民地时

    会遇到什么样的问题。他们只知道自己的目标和制约条件,但是不知道旅途

    的细节。

    ③ 这世界真小,Andy还是William Clark的远亲呢。

    软件项目中的设计也与此类似。在没有穿越殖民地的时候,你不可能知道会

    出现什么情况。所以,不要事先浪费时间规划如何徒步穿越河流,只有当你

    走到河岸边的时候,才能真正评估和规划如何穿越。只有到那时,你才开始

    真正的战术设计。

    不要一开始就进行战术设计,它的重点是集中在单个的方法或数据类型上。

    这时,更适合讨论如何设计类的职责。因为这仍然是一个高层次、面向目标的设计。事实上,CRC(类—职责—协作)卡片的设计方法就是用来做这个

    事情的。每个类按照下面的术语描述。

    类名。

    职责:它应该做什么?

    协作者:要完成工作它要与其他什么对象一起工作?

    如何知道一个设计是好的设计,或者正合适?代码很自然地为设计的好坏提

    供了最好的反馈。如果需求有了小的变化,它仍然容易去实现,那么它就是

    好的设计。而如果小的需求变化就带来一大批基础代码的破坏,那么设计就

    需要改进。

    好设计是一张地图,它也会进化 。设计指引你向正确的方向前进,它不是殖民地,它不应该标识具体的路线。你不要被设计(或者设计师)

    操纵。

    切身感受

    好的设计应该是正确的,而不是精确的。也就是说,它描述的一切必须是正

    确的,不应该涉及不确定或者可能会发生变化的细节。它是目标,不是具体

    的处方。

    平衡的艺术

    “不要在前期做大量的设计”并不是说不要设计。只是说在没有经过真

    正的代码验证之前,不要陷入太多的设计任务。当对设计一无所知的时

    候,投入编码也是一件危险的事。如果深入编码只是为了学习或创造原

    型,只要你随后能把这些代码扔掉,那也是一个不错的办法。

    即使初始的设计到后面不再管用,你仍需设计:设计行为是无价的。正

    如美国总统艾森豪威尔所说:“计划是没有价值的,但计划的过程 是

    必不可少的④。”在设计过程中学习是有价值的,但设计本身也许没有

    太大的用处。

    ④ 1957年的演讲稿。

    白板、草图、便利贴都是非常好的设计工具。复杂的建模工具只会让你

    分散精力,而不是启发你的工作。12 合理地使用技术

    “你开始了一个新的项目,在你面前有一长串关于新技术和应用框架

    的列表。这些都是好东西,你真的需要使用列表中所有的技术。想一

    想,你的简历上将留下漂亮的一笔,用那些伟大的框架,你的新应用

    将具有极高技术含量。”

    盲目地为项目选择技术框架,就好比是为了少交税而生孩子

    Blindly picking a framework is like having kids to save

    taxes

    从前,作者之一Venkat的同事Lisa向他解释自己的提议:她打算使用EJB。

    Venkat表示对EJB有些顾虑,觉得它不适合那个特殊的项目。然后Lisa回答

    道:“我已经说服了我们经理,这是正确的技术路线,所以现在不要再

    扔‘炸弹’了。”这是一个典型的“简历驱动设计”的例子,之所以选择这

    个技术,是因为它很美,也许还能提高程序员的技能。但是,盲目地为项目

    选择技术框架,就好比是为了节省税款而生孩子,这是没有道理的。

    在考虑引入新技术或框架之前,先要把你需要解决的问题找出来。你的表述

    方式不同,会让结果有很大差异。如果你说“我们需要xyzzy 技术,是因

    为……”,那么就不太靠谱。你应该这样说:“……太难了”或者是“……

    花的时间太长了”,或者类似的句子。找到了需要解决的问题,接下来就要

    考虑如下方面。

    这个技术框架真能解决这个问题吗? 是的,也许这是显而易见的。但

    是,这个技术真能解决你面临的那个问题吗?或者,更尖锐一点说,你

    是如何评估这个技术的?是通过市场宣传还是道听途说?要确保它能解

    决你的问题,并没有任何的毒副作用。如果需要,先做一个小的原型。

    你将会被它拴住吗? 一些技术是贼船,一旦你使用了它,就会被它套

    牢,再也不可能回头了。它缺乏可取消性 (查阅[HT00]),当条件发

    生变化时,这可能对项目有致命打击。我们要考虑它是开放技术还是专

    利技术,如果是开放的技术,那又开放到什么程度?

    维护成本是多少? 会不会随着时间的推移,它的维护成本会非常昂

    贵?毕竟,方案的花费不应该高于要解决的问题,否则就是一次失败的

    投资。我们听说,有个项目的合同是支持一个规则引擎,引擎一年的维

    护费用是5万美元,但是这个数据库只有30条规则。这也太贵了。当你在考察一个框架(或者任何技术)的时候,也许会被它提供的各种功能

    吸引。接着,在验证是否使用这个框架的时候,你可能只会考虑已经发现的

    另外一些功能。但是,你真的需要这些功能吗?也许为了迎合你发现的功

    能,你正在为它们找问题。这很像站在结账处一时冲动而买些无用的小零碎

    (那也正是商场把那些小玩意儿放到那里的原因)。

    不久前,Venkat遇到了一个项目。咨询师Brad把一个专有框架卖给了这个项

    目的管理者。在Venkat看来,这个框架本身也许还有点儿意思,但是它根本

    不适合这个项目。

    尽管如此,管理者却坚决认为他们要使用它。Venkat非常礼貌地停手不干

    了。他不想成为绊脚石,阻碍他们的工作进度。一年之后项目还没有完成

    ——他们花了好几个月的时间编写代码来维护这个框架,为了适应这个框

    架,他们还修改了自己的代码。

    Andy有过相似的经历:他的客户想完全透明地利用开源,他们拥有“新技术

    大杂烩”,其中的东西太多,以至于无法让所有的部分协同工作。

    不要开发你能下载到的东西

    Don’t build what you can download

    如果你发现自己在做一些花哨的东西(比如从头创建自己的框架),那就醒

    醒吧,闻闻烟味有多大,马上该起火了。你的代码写得越少,需要维护的东

    西就越少。

    例如,如果你想开发自己的持久层框架,记住Ted Neward的评论:对象—关

    系的映射就是计算机科学的越南战场①。你可以把更多的时间和精力投入到

    应用的开发——领域或具体应用中。

    ① Ted Neward曾写过The Vietnam of Computer Science 著名文章,逐

    一探讨了对象—关系映射的缺点。——编者注

    根据需要选择技术 。首先决定什么是你需要的,接着为这些具体的

    问题评估使用技术。对任何要使用的技术,多问一些挑剔的问题,并真实

    地作出回答。

    切身感受新技术就应该像是新的工具,可以帮助你更好地工作,它自己不应该成为你

    的工作。

    平衡的艺术

    也许在项目中真正评估技术方案还为时太早。那就好。如果你在做系统

    原型并要演示给客户看,也许一个简单的散列表就可以代替数据库了。

    如果你还没有足够的经验,不要急于决定用什么技术。

    每一门技术都会有优点和缺点,无论它是开源的还是商业产品、框架、工具或者语言,一定要清楚它的利弊。

    不要开发那些你容易下载到的东西。虽然有时需要从最基础开发所有你

    需要的东西,但那是相当危险和昂贵的。

    13 保持可以发布

    “我们刚试用的时候发现了一个问题,你需要立即修复它。放下你手

    头的工作,去修复那个刚发现的问题,不需要经过正规的程序。不用

    告诉其他任何人——赶快让它工作就行了。”

    这听起来似乎没什么问题。有一个关键修复的代码必须要提交到代码库。这

    只是一件小事,而且又很紧急,所以你就答应了。

    修复工作成功地完成了。你提交了代码,继续回到以前那个高优先级的任务

    中。忽然一声尖叫。太晚了,你发现同事提交的代码和你的代码发生了冲

    突,现在你使得每个人都无法使用系统了。这将会花费很多精力(和时间)

    才能让系统重新回到可发布的状态。现在你有麻烦了。你必须告诉大家,你

    不能交付你承诺的修复代码了。而魔鬼在嘲笑:“哈哈哈!”

    这时候,你的处境会很糟糕:系统无法发布了。你弄坏了系统,也许会带来

    更糟糕的后果。

    1836年,当时的墨西哥总统安东尼奧·洛佩斯·德·圣安那将军,率领部队

    穿越得克萨斯州西部,追赶败退的萨姆·休斯顿将军。当圣安那的部队到达

    得克萨斯州东南方向的布法罗河岸的沼泽地带的时候,他命令自己的部队就

    地休息。传说中认为他是太过自信,甚至没有安排哨兵。就在那个傍晚,休

    斯顿发动了突然袭击,这时圣安那的部队已经来不及编队了。他们溃不成

    军,输掉了这场决定性的战争,从此永远改变了得克萨斯州的历史①。① http:www.sanjacinto-museum.orgThe_BattleApril_21st_1836。

    已提交的代码应该随时可以行动

    Checked-in code is always ready for action

    任何时候只要你没有准备好,那就是敌人进攻你的最佳时机。好好想一想,你的项目进入不可发布状态的频率是多少?你的源代码服务器中的代码,是

    不是像圣安那在那个决定性的黄昏一样——没有进行编队,遇到紧急情况无

    法立即启动。

    在团队里工作,修改一些东西的时候必须很谨慎。你要时刻警惕,每次改动

    都会影响系统的状态和整个团队的工作效率。在办公室的厨房里,你不能容

    忍任何人乱丢垃圾,为什么就可以容忍一些人给项目带来垃圾代码呢?

    下面是一个简单的工作流程,可以防止你提交破坏系统的代码。

    在本地运行测试 。先保证你完成的代码可以编译,并且能通过所有的

    单元测试。接着确保系统中的其他测试都可以通过。

    检出最新的代码 。从版本控制系统中更新代码到最新的版本,再编译

    和运行测试。这样往往会发现让你吃惊的事情:其他人提交的新代码和

    你的代码发生了冲突。

    提交代码 。现在是最新的代码了,并且通过了编译和测试,你可以提

    交它们了。

    在做上面事情的时候,也许你会遇到这样一个问题——其他人提交了一些代

    码,但是没有通过编译或者测试。如果发生了这样的事情,要立即让他们知

    道,如果有需要,可以同时警告其他的同事。当然,最好的办法是,你有一

    个持续集成 系统,可以自动集成并报告集成结果。

    这听起来似乎有点恐怖,其实很简单。持续集成系统就是在后台不停地检

    出、构建和测试代码的应用。你可以自己使用脚本快速实现这样的方式,但

    如果你选择已有的免费、开源的解决方案,它们会提供更多的功能且更加稳

    定。有兴趣的话,可以看一看Martin Fowler的文章②,或者是Mike Clark

    编著的图书《项目自动化之道》[Cla04]。

    ②

    http:www.martinfowler.comarticlescontinuousIntegration.html。

    再深入一点,假设你得知即将进行的一次重大修改很可能会破坏系统,不要

    任其发生,应该认真地警告大家,在代码提交之前,找出可以避免破坏系统

    的方法。选择可以帮助你平滑地引入和转换这些修改的方法,从而在开发过

    程中,系统可以得到持续的测试和反馈。

    虽然保持系统可发布非常重要,但不会总是那么容易,例如,修改了数据库

    的表结构、外部文件的格式,或者消息的格式。这些修改,通常会影响应用

    的大部分代码,甚至导致应用暂时不可用,直到大量的代码修改完成。尽管

    如此,你还是有办法减轻这样的痛苦。

    为数据库的表结构、外部文件,甚至引用它的API提供版本支持,这样所有

    相关变化都可以进行测试。有了版本功能,所做的变化可以与其他代码基相

    隔离,所以应用的其他方面仍然可以继续开发和测试。

    你也可以在版本控制系统中添加一个分支,专门处理这个问题(使用分支需

    要十分小心,不好的分支也许会给你带来更多的麻烦。详情可以查阅《版本

    控制之道——CVS》或《版本控制之道——Subversion》)。

    保持你的项目时刻可以发布 。保证你的系统随时可以编译、运行、测试并立即部署。

    切身感受

    你会觉得,不管什么时候,你的老板、董事长、质量保障人员、客户或者你

    的配偶来公司参观项目的时候,你都能很自信并毫不犹豫地给他们演示最新

    构建的软件。你的项目一直处于可以运行的稳定状态。

    平衡的艺术

    有时候,做一些大的改动后,你无法花费太多的时间和精力去保证系统

    一直可以发布。如果总共需要一个月的时间才能保证它一周内可以发

    布,那就算了。但这只应该是例外,不能养成习惯。

    如果你不得不让系统长期不可以发布,那就做一个(代码和架构的)分

    支版本,你可以继续进行自己的实验,如果不行,还可以撤销,从头再

    来。千万不能让系统既不可以发布,又不可以撤销。

    14 提早集成,频繁集成“只要没有到开发的末尾阶段,就不要过早地浪费时间去想如何集成

    你的代码,至少也要等开发差不多的时候,才开始考虑它。毕竟,还

    没有完成开发,为什么要操心集成的事情呢!在项目的末尾,你有充

    裕的时间来集成代码。”

    我们说过,敏捷的一个主要特点就是持续开发,而不是三天打鱼两天晒网似

    地工作。特别是在几个人一起开发同一个功能的时候,更应该频繁地集成代

    码。

    很多开发者用一些美丽的借口,推迟集成的时间。有时,不过是为了多写一

    些代码,或者是另一个子系统还有很多的工作要完成。他们很容易就会这样

    想:“现在手头上的工作压力够大了,到最后我才能做更多的工作,才能考

    虑其他人代码。”经常会听到这样的借口:“我没有时间进行集成”或者

    “在我机器上设置集成环境太费事了,我现在不想做它”。

    但是,在产品的开发过程中,集成是一个主要的风险区域。让你的子系统不

    停地增长,不去做系统集成,就等于一步一步把自己置于越来越大的风险

    中,世界没有了你仍然会转动,潜在的分歧会继续增加。相反,尽可能早地

    集成也更容易发现风险,这样风险及相关的代价就会相当低。而等的时间越

    长,你也就会越痛苦。

    作者之一Venkat小时候生活在印度钦奈市(Chennai),经常赶火车去学

    校。像其他的大城市一样,印度的交通非常拥挤。他每次必须在车还没有停

    稳的时候,就跳上去或者跳下来。但,你不能从站的地方一下子跳上运行的

    火车,我们在物理课上学习过这种运动定律。而应该是,首先你要沿着火车

    行驶的方向跑,边跑边抓住火车上的扶手,然后跳入火车中。

    软件集成就像这一样。如果你不断地独立开发,忽然有一天跳到集成这一

    步,千万不要为受到打击而吃惊。也许你自己在项目中就有这样的体会:每

    次到项目结束的时候都觉得非常不爽,大家需要日日夜夜地进行集成。

    你能集成并且独立

    集成和独立不是互相矛盾的,你可以一边进行集成,一边进行独立开发。

    使用mock对象来隔离对象之间的依赖关系,这样在集成之前就可以先做测

    试,用一个mock对象模拟真实的对象(或者子系统)。就像是拍电影时在

    光线的掩饰下使用替身一样,mock对象就是真实对象的替身,它并不提供

    真实对象的功能,但是它更容易控制,能够模仿需要的行为,使测试更加

    简单。你可以使用mock对象,编写独立的单元测试,而不需要立刻就集成和测试

    其他系统,只有当你自信它能工作的时候,才可以开始集成。

    当你在公司昏天黑地地加班时,唯一的好处就是可以享受到免费的披萨。

    独立开发和早期集成之间是具有张力的。当你独立开发时,会发现开发速度

    更快,生产率更高,你可以更有效地解决出现的问题(见第136页,习惯

    35)。但那并不意味着要你避免或延迟集成(见本页侧边栏)。你一般需要

    每天集成几次,最好不要2~3天才集成一次。

    决不要做大爆炸式的集成

    Never accept big-bang integration

    当早期就进行集成的时候,你会看到子系统之间的交互和影响,你就可以估

    算它们之间通信和共享的信息数据。你越早弄清楚这些问题,越早解决它

    们,工作量就越小。就好比是,刚开始有3个开发者,开发着5万行的代码,后来是5000个开发者进行3000万行代码的开发。相反,如果你推迟集成的时

    间,解决这些问题就会变得很难,需要大量和大范围地修改代码,会造成项

    目延期和一片混乱。

    提早集成,频繁集成 。代码集成是主要的风险来源。要想规避这个

    风险,只有提早集成,持续而有规律地进行集成。

    切身感受

    如果你真正做对了,集成就不再会是一个繁重的任务。它只是编写代码周期

    中的一部分。集成时产生的问题,都会是小问题并且容易解决。

    平衡的艺术

    成功的集成就意味着所有的单元测试不停地通过。正如医学界希波克拉

    底的誓言:首先,不要造成伤害。

    通常,每天要和团队其他的成员一起集成代码好几次,比如平均每天

    5~10次,甚至更多。但如果你每次修改一行代码就集成一次,那效用肯

    定会缩水。如果你发现自己的大部分时间都在集成,而不是写代码,那

    你一定是集成得过于频繁了。

    如果你集成得不够频繁(比如,你一天集成一次,一周一次,甚至更

    糟),也许就会发现整天在解决代码集成带来的问题,而不是在专心写代码。如果你集成的问题很大,那一定是做得不够频繁。

    对那些原型和实验代码,也许你想要独立开发,而不要想在集成上浪费

    时间。但是不能独立开发太长的时间。一旦你有了经验,就要快速地开

    始集成。

    15 提早实现自动化部署

    “没问题,可以手工安装产品,尤其是给质量保证人员安装。而且你

    不需要经常自己动手,他们都很擅长复制需要的所有文件。”

    系统能在你的机器上运行,或者能在开发者和测试人员的机器上运行,当然

    很好。但是,它同时也需要能够部署在用户的机器上。如果系统能运行在开

    发服务器上,那很好,但是它同时也要运行在生产环境中。

    这就意味着,你要能用一种可重复和可靠的方式,在目标机器上部署你的应

    用。不幸的是,大部分开发者只会在项目的尾期才开始考虑部署问题。结果

    经常出现部署失败,要么是少了依赖的组件,要么是少了一些图片,要么就

    是目录结构有误。

    如果开发者改变了应用的目录结构,或者是在不同的应用之间创建和共享图

    片目录,很可能会导致安装过程失败。当这些变化在人们印象中还很深的时

    候,你可以快速地找到各种问题。但是几周或者几个月之后查找它们,特别

    是在给客户演示的时候,可就不是一件闹着玩的事情了。

    质量保证人员应该测试部署过程

    QA should test deployment

    如果现在你还是手工帮助质量保证人员安装应用,花一些时间,考虑如何将

    安装过程自动化。这样,只要用户需要,你就可以随时为他们安装系统。要

    提早实现它,这样让质量保证团队既可以测试应用,又可以测试安装过程

    ①。如果还是手工安装应用,那么最后把应用部署到生产环境时会发生什么

    呢?就算公司给你加班费,你也不愿意为不同用户的机器或不同地点的服务

    器上一遍又一遍地安装应用。

    ① 确保他们能提前告诉你运行的软件版本,避免出现混乱。

    有了自动化部署系统后,在项目开发的整个过程中,会更容易适应互相依赖

    的变化。很可能你在安装系统的时候,会忘记添加需要的库或组件——在任意一台机器上运行自动化安装程序,你很快就会知道什么丢失了。如果因为

    缺少了一些组件或者库不兼容而导致安装失败,这些问题会很快浮现出来。

    Andy如是说……

    从第一天起就开始交付

    一开始就进行全面部署,而不是等到项目的后期,这会有很多好处。事实

    上,有些项目在正式开发之前,就设置好了所有的安装环境。

    在我们公司,要求大家为预期客户实现一个简单的功能演示——验证一个

    概念的可行性。即使项目还没有正式开始,我们就有了单元测试、持续集

    成和基于窗口的安装程序。这样,我们就可以更容易更简单地给用户交付

    这个演示系统:用户所要做的工作,就是从我们的网站上点击一个链接,然后就可以自己在各种不同的机器上安装这个演示系统了。

    在签约之前,就能提供出如此强大的演示,这无疑证明了我们非常专业,具有强大的开发能力。

    一开始就实现自动化部署应用 。使用部署系统安装你的应用,在不

    同的机器上用不同的配置文件测试依赖的问题。质量保证人员要像测试应

    用一样测试部署。

    切身感受

    这些工作都应该是无形的。系统的安装或者部署应该简单、可靠及可重复。

    一切都很自然。

    平衡的艺术

    一般产品在安装的时候,都需要有相应的软、硬件环境。比如,Java或

    Ruby的某个版本、外部数据库或者操作系统。这些环境的不同很可能会

    导致很多技术支持的电话。所以检查这些依赖关系,也是安装过程的一

    部分。

    在没有询问并征得用户的同意之前,安装程序绝对不能删除用户的数

    据。

    部署一个紧急修复的bug应该很简单,特别是在生产服务器的环境中。

    你知道这会发生,而且你不想在压力之下,在凌晨3点半,你还在手工部署系统。

    用户应该可以安全并且完整地卸载安装程序,特别是在质量保证人员的

    机器环境中。

    如果维护安装脚本变得很困难,那很可能是一个早期警告,预示着——

    很高的维护成本(或者不好的设计决策)。

    如果你打算把持续部署系统和产品CD或者DVD刻录机连接到一起,你就

    可以自动地为每个构建制作出一个完整且有标签的光盘。任何人想要最

    新的构建,只要从架子上拿最上面的一张光盘安装即可。

    16 使用演示获得频繁反馈

    “这不是你的过错,问题出在我们的客户——那些麻烦的最终客户和

    用户身上。他们不停地更改需求,导致我们严重地延期。他们一次就

    应该想清楚所有想要的东西,然后把这些需求给我们,这样我们才能

    开发出令他们满意的系统。这才是正确的工作方式。”

    需求就像是流动着的油墨

    Requirements are as fluid as ink

    你时常会听到一些人想要冻结需求。但是,现实世界中的需求就像是流动着

    的油墨①。你无法冻结需求,正如你无法冻结市场、竞争、知识、进化或者

    成长一样。就算你真的冻结了,也很可能是冻结了错的东西。如果你期望用

    户在项目开始之前,就能给你可靠和明确的需求,那就大错特错了,赶快醒

    醒吧!

    ① Edward V. Berard曾经指出:“如果需求能被冻结,那么开发软件就

    如在冻冰上走路一样简单。”

    没有人的思想和观点可以及时冻结,特别是项目的客户。就算是他们已经告

    诉你想要的东西了,他们的期望和想法还是在不停地进化——特别是当他们

    在使用新系统的部分功能时,他们才开始意识到它的影响和可能发生的问

    题。这就是人的本性。

    作为人类,不管是什么事情,我们都能越做越好,不过是以缓慢而逐步的方

    式。你的客户也一样。在给了你需求之后,他们会不停地研究这些功能,如

    何才能让它们变得更好使用。如果,你觉得自己要做的所有工作就是按照用户最初的需求,并实现了它们,但是在交付的时候,需求已经发生了变化,你的软件可能不会令他们满意。在软件开发过程中,你将自己置于最大的风

    险中:你生产出了他们曾经要求过的软件,但却不是他们现在真正想要的。

    那最后的结果就是:惊讶、震惊和失望,而不是满意。

    几年前的一次数值分析课上,老师要求Venkat使用一些偏微分方程式模拟宇

    宙飞船的运行轨线。

    程序基于时间t 的坐标点,计算出在时间t +δ 的位置。程序最后绘出来的

    轨线图就是如图4-1中的虚线。

    图4-1 计算宇宙飞船的运行轨线

    我们发现,估算出来的宇宙飞船位置远远地偏离了它的真实位置。万有引力

    不是只在我们计算的坐标点上才起作用。实际上,万有引力一直起作用:它

    是连续的,而不是离散的。由于忽略了点之间的作用力,我们的计算不断引

    入了误差,所以宇宙飞船最后到达了错误的地方。

    缩小点之间的间隔(就是δ 的值),再运行计算程序,误差就会减少。这

    时,估算的位置(如图4-1中的实线)就和实际位置很接近了。同理,你的客户的期望就像宇宙飞船的实际位置。软件开发的成功就在于最

    后你离客户的期望有多近。你计算的每个精确位置,就是一个给客户演示目

    前已经完成功能的机会,也正是得到用户反馈的时候。在你动身进入下一段

    旅程的时候,这些反馈可以用来纠正你的方向。

    我们经常看到,给客户演示所完成功能的时间与得到客户需求的时间间隔越

    长,那么你就会离最初需求越来越远。

    应该定期地,每隔一段时间,例如一个迭代的结束,就与客户会晤,并且演

    示你已经完成的功能特性。

    如果你能与客户频繁协商,根据他们的反馈开发,每个人都可以从中受益。

    客户会清楚你的工作进度。反过来,他们也会提炼需求,然后趁热反馈到你

    的团队中。这样,他们就会基于自己进化的期望和理解为你导航,你编写的

    程序也就越来越接近他们的真实需求。客户也会基于可用的预算和时间,根

    据你们真实的工作进度,排列任务的优先级。

    较短的迭代周期,会对频繁的反馈有负面影响吗?在宇宙飞船轨线的程序

    中,当δ降低的时候,程序运行就要花费更长的时间。也许你会觉得,使用

    短的迭代周期会使工作变慢,延迟项目的交付。

    让我们从这个角度思考:两年来一直拼命地开发项目,直到快结束的时候,你和你的客户才发现一个基础功能有问题,而且它是一个核心的需求。你以

    为缺货订单是这样处理的,但这完全不是客户所想的东西。现在,两年之

    后,你完成了这个系统,写下了数百万行的代码,却背离了客户的期望。再

    怎么说,两年来辛苦写出的代码有相当大部分要重写,代价是沉重的。

    相反,如果你一边开发,一边向他们演示刚完成的功能。项目进展了两个月

    的时候,他们说:“等一下,缺货订单根本不是这么一回事。”于是,召开

    一个紧急会议:你重新审查需求,评估要做多大的改动。这时只要付很少的

    代价,就可以避免灾难了。

    要频繁地获得反馈。如果你的迭代周期是一个季节或者一年(那就太长

    了),就应把周期缩短到一周或者两周。完成了一些功能和特征之后,去积

    极获得客户的反馈。

    Andy如是说……

    维护项目术语表

    不一致的术语是导致需求误解的一个主要原因。企业喜欢用看似普遍浅显

    的词语来表达非常具体、深刻的意义。我经常看到这样的事情:团队中的程序员们,使用了和用户或者业务人员

    不同的术语,最后因为“阻抗失调”导致bug和设计错误。

    为了避免这类问题,需维护一份项目术语表。人们应该可以公开访问它,一般是在企业内部网或者Wiki上。这听起来似乎是一件小事情——只是一

    个术语列表及其定义。但是,它可以帮助你,确保你真正地和用户进行沟

    通。

    在项目的开发过程中,从术语表中为程序结构——类、方法、模型、变量

    等选择合适的名字,并且要检查和确保这些定义一直符合用户的期望。

    清晰可见的开发 。在开发的时候,要保持应用可见(而且客户心中

    也要了解)。每隔一周或者两周,邀请所有的客户,给他们演示最新完成

    的功能,积极获得他们的反馈。

    切身感受

    项目启动了一段时间之后,你应该进入一种舒适的状态,团队和客户建立了

    一种健康的富有创造性的关系。

    突发事件应极少发生。客户应该能感觉到,他们可以在一定程度上控制项目

    的方向。

    跟踪问题

    随着项目的进展,你会得到很多反馈——修正、建议、变更要求、功能增

    强、bug修复等。要注意的信息很多。随机的邮件和潦草的告示帖是无法

    应付的。所以,要有一个跟踪系统记录所有这些日志,可能是用Web界面

    的系统。更多详情参阅Ship it! [RG05]。

    平衡的艺术

    当你第一次试图用这种方法和客户一起工作的时候,也许他们被这么多

    的发布吓到了。所以,要让他们知道,这些都是内部 的发布(演

    示),是为了他们自己的利益,不需要发布给全部的最终用户。

    一些客户,也许会觉得没有时间应付每天、每周甚至是每两周的会议。

    毕竟,他们还有自己的全职工作。所以要尊重客户的时间。如果客户只可以接受一个月一次会议,那么就

    定一个月。

    一些客户的联络人的全职工作就是参加演示会议。他们巴不得每隔1小

    时就有一次演示和反馈。你会发现这么频繁的会议很难应付,而且还要

    开发代码让他们看。缩减次数,只有在你做完一些东西可以给他们演示

    的时候,大家才碰面。

    演示是用来让客户提出反馈的,有助于驾驭项目的方向。如果缺少功能

    或者稳定性的时候,不应该拿来演示,那只能让人生气。可以及早说明

    期望的功能:让客户知道,他们看到的是一个正在开发中的应用,而不

    是一个最终已经完成的产品。

    17 使用短迭代,增量发布

    “我们为后面的3年制定了漂亮的项目计划,列出了所有的任务和可交

    付的时间表。只要我们那时候发布了产品,就可以占领市场。”

    统一过程和敏捷方法都使用迭代和增量 开发①。使用增量 开发一次开发应

    用功能的几个小组。每一轮的开发都是基于前一次的功能,增加为产品增值

    的新功能。这时,你就可以发布或者演示产品。

    ① 但是,所有减肥方案都会建议你应该少吃多做运动。然而,每份关于

    如何达到目标的计划都会不尽相同。

    迭代开发是,在小且重复的周期里,你完成各种开发任务:分析、设计、实

    现、测试和获得反馈,所以叫作迭代。

    迭代的结束就标记一个里程碑。这时,产品也许可用,也许不可用。在迭代

    结束时,新的功能全部完成,你就可以发布,让用户真正地使用,同时提供

    技术支持、培训和维护方面的资源。每次增加的新功能都会包含多次迭代。

    给我一份详细的长期计划,我就会给你一个注定完蛋的项目

    Show me a detailed long-term plan,and I’ll show you a

    project that’s doomed

    根据Capers Jones的格言:“……大型系统的开发是一件非常危险的事

    情。”大型系统更容易失败。它们通常不遵守迭代和增量开发的计划,或者

    迭代时间太长(更多关于迭代和演进开发的讨论,以及和风险的关系、生产率和缺点,可以查阅Agile and Iterative Development:A Manager’s

    Guide [Lar04]一书)。Larman指出,软件开发不是精细的制造业,而是创

    新活动。规划几年之后客户才能真正使用的项目注定是行不通的。

    对付大项目,最理想的办法就是小步前进,这也是敏捷方法的核心。大步跳

    跃大大地增加了风险,小步前进才可以帮助你很好地把握平衡。

    在你周围,可以看到很多迭代和增量开发的例子。比如W3C(万维网联盟)

    提出的XML规范DTD(Document Type Definitions,文档类型定义),它用

    来定义XML文档的词汇和结构,作为原规范的部分发布。虽然在DTD设计的时

    候就解决了很多问题,但是在真正使用的时候,又显现出很多问题和限制。

    基于用户的反馈对规范就有了更深一层的理解,这样就诞生了更加高效的第

    二代解决方案,例如Schema。如果他们一开始就试图进行一些完美的设计,也许就看不到XML成为今天的主流了——我们通过提早发布获得了灼见和经

    验。

    大部分用户都是希望现在就有一个够用的软件,而不是在一年之后得到一个

    超级好的软件(可以参见《程序员修炼之道——从小工到专家》“足够好的

    软件 ”一节[HT00])。确定使产品可用的核心功能,然后把它们放在生产

    环境中,越早交到用户的手里越好。

    根据产品的特性,发布新的功能需要几周或者几个月的时间。如果是打算一

    年或者两年再交付,你就应该重新评估和重新计划。也许你要说,构建复杂

    的系统需要花费时间,你无法用增量的方式开发一个大型的系统。如果这种

    情况成立,就不要生产大的系统。可以把它分解成一块块有用的小系统——

    再进行增量开发。即使是美国国家航空航天局(NASA)也使用迭代和增量开

    发方式开发用于航天飞机的复杂软件(参见Design, Development,Integration: Space Shuttle Primary Flight Software System

    [MR84])。

    询问用户,哪些是使产品可用且不可缺少的核心功能。不要为所有可能需要

    的华丽功能而分心,不要沉迷于你的想象,去做那些华而不实的用户界面。

    有一堆的理由,值得你尽快把软件交到用户手中:只要交到用户手里,你就

    有了收入,这样就有更好的理由继续为产品投资了。从用户那里得到的反

    馈,会让我们进一步理解什么是用户真正想要的,以及下一步该实现哪些功

    能。也许你会发现,一些过去认为重要的功能,现在已经不再重要了——我

    们都知道市场的变化有多快。尽快发布你的应用,迟了也许它就没有用了。

    使用短迭代和增量开发,可以让开发者更加专注于自己的工作。如果别人告

    诉你有一年的时间来完成系统,你会觉得时间很长。如果目标很遥远,就很难让自己去专注于它。在这个快节奏的社会,我们都希望更快地得到结果,希望更快地见到有形的东西。这不一定是坏事,相反,它会是一件好事,只

    要把它转化成生产率和正面的反馈。

    图4-2描述了敏捷项目主要的周期关系。根据项目的大小,理想的发布周期

    是几周到几个月。在每个增量开发周期里,应该使用短的迭代(不应该超过

    两周)。每个迭代都要有演示,选择可能提供反馈的用户,给他们每人一份

    最新的产品副本。

    图4-2 嵌套敏捷开发周期

    增量开发 。发布带有最小却可用功能块的产品。每个增量开发中,使用1~4周左右迭代周期。

    切身感受短迭代让人感觉非常专注且具效率。你能看到一个实际并且确切的目标。严

    格的最终期限迫使你做出一些艰难的决策,没有遗留下长期悬而未决的问

    题。

    平衡的艺术

    关于迭代时间长短一直是一个有争议的问题。Andy曾经遇到这样一位客

    户:他们坚持认为迭代就是4周的时间,因为这是他们学到的。但他们

    的团队却因为这样的步伐而垂死挣扎,因为他们无法在开发新的代码的

    同时又要维护很多已经完成了的代码。解决方案是,在每4周的迭代中

    间安排一周的维护任务。没有规定说迭代必须要紧挨着下一个迭代。

    如果每个迭代的时间都不够用,要么是任务太大,要么是迭代的时间太

    短(这是平均数据,不要因为一次迭代的古怪情况而去调整迭代时

    间)。把握好自己的节奏。

    如果发布的功能背离了用户的需要,那么多半是因为迭代的周期太长

    了。用户的需要、技术和我们对需求的理解,都会随着时间的推移而变

    化,在项目发布的时候,需要清楚地反映出这些变化。如果你发现自己

    工作时还带有过时的观点和陈腐的想法,那么很可能你等待太长时间做

    调整了。

    增量的发布必须是可用的,并且能为用户提供价值。你怎么知道用户会

    觉得有价值呢?这当然要去问用户。

    18 固定的价格就意味着背叛承诺

    “对这个项目,我们必须要有固定的报价。虽然我们还不清楚项目的

    具体情况,但仍要有一个报价。到星期一,我需要整个团队的评估,并且我们必须要在年末交付整个项目。”

    固定价格的合同会是敏捷团队的一大难题。我们一直在谈论如何用持续、迭

    代和增量的方式工作。但是现在却有些人跑过来,想提早知道它会花费多少

    时间及多少成本。

    从客户方来看,这完全是理所应当的。客户觉得做软件就好比是盖一栋楼

    房,或者是铺设一个停车场,等等。为什么软件不能像建筑业等其他传统的

    行业一样呢?也许它真的与建筑有很多相似之处——真正的建筑行业,但不是我们想象中

    的建筑业。根据英国1998年的一个研究,由于错误而返工的成本大约占整个

    项目成本的30%①。这不是因为客户的需求变化,也不是物理定律的变化,而是一些简单错误。比如,横梁太短,窗户洞太大,等等。这些都是简单并

    且为人熟悉的错误。

    ① Rethinking Construction: The Report of the Construction Task

    Force ,1998年1月1日,英国副首相办公室地方政府和地区运输部文件。

    软件项目会遭遇各种各样的小错误,还要加上基础需求的变化(不,我要的

    不是一个工棚,而是一栋摩天大楼),不同个体和团队的能力差别非常巨大

    (20倍,甚至更多),当然,还不停地会有新技术出现(从现在开始,钉子

    就变成圆形的了)。

    固定的价格就是保证要背叛承诺

    A fixed price guarantees a broken promise

    软件项目天生就是变化无常的,不可重复。如果要提前给出一个固定的价

    格,就几乎肯定不能遵守开发上的承诺。那么我们有什么可行的办法呢?我

    们能做更精确的评估吗?或者商量出另外一种约定。

    根据自己的处境,选择不同的战略。如果你的客户一定要你预先确定项目的

    报价(比如政府合约),那么可能你需要研究一些重型的评估技术,比如

    COCOMO模型或者功能点分析法(Function Point analysis)。但它们不属

    于敏捷方法的范畴,并且使用它们也要付出代价。如果这个项目本质上和另

    一个项目十分相似,并且是同一个团队开发的,那么你就好办了:为一个用

    户开发的简单网站,与下一个会非常相似。

    但是,很多项目并不像上面所说的那么如意。大部分项目都是业务应用,一

    个用户和另一个用户都有着巨大的差别。项目的发掘和创造需要很多配合工

    作。或许你可以提供稍有不同的安排,试试下面的办法。

    1. 主动提议先构建系统最初的、小的和有用的部分(用建筑来打个比方,就是先做一个车库)。挑选一系列小的功能,这样完成第一次交付应该

    不多于6~8周。向客户解释,这时候还不是要完成所有的功能,而是要

    足够一次交付,并能让用户真正使用。

    2. 第一个迭代结束时客户有两个选择:可以选择一系列新的功能,继续进

    入下一个迭代;或者可以取消合同,仅需支付第一个迭代的几周费用,他们要么把现在的成果扔掉,要么找其他的团队来完成它。3. 如果他们选择继续前进。那么这时候,应该就能很好地预测下一个迭代

    工作。在下一个迭代结束的时候,用户仍然有同样的选择机会:要么现

    在停止,要么继续下一个迭代。

    对客户来说,这种方式的好处是项目不可能会死亡。他们可以很早地看到工

    作的进度(或者不足之处)。他们总是可以控制项目,可以随时停止项目,不需要缴纳任何的违约金。他们可以控制先完成哪些功能,并能精确地知道

    需要花费多少资金。总而言之,客户会承担更低的风险。

    而你所做的就是在进行迭代和增量开发。

    基于真实工作的评估 。让团队和客户一起,真正地在当前项目中工

    作,做具体实际的评估。由客户控制他们要的功能和预算。

    切身感受

    你的评估数据会在整个项目中发生变化——它们不是固定的。但是,你会觉

    得自信心在不断增加,你会越来越清楚每个迭代可以完成的工作。随着时间

    的推移,你的评估能力会不断地提高。

    平衡的艺术

    如果你对答案不满意,那么看看你是否可以改变问题。

    如果你是在一个基于计划的非敏捷环境中工作,那么要么考虑一个基于

    计划且非敏捷的开发方法,要么换一个不同的环境。

    如果你在完成第一个迭代开发之前,拒绝做任何评估,也许你会失去这

    个合同,让位于那些提供了评估的人,无论他们做了多么不切实际的承

    诺。

    敏捷不是意味着“开始编码,我们最终会知道何时可以完成”。你仍然

    需要根据当前的知识和猜想,做一个大致的评估,解释如何才能到达这

    个目标,并给出误差范围。

    如果你现在别无选择,你不得不提供一个固定的价格,那么你需要学到

    真正好的评估技巧。

    也许你会考虑在合同中确定每个迭代的固定价格,但迭代的数量是可以

    商量的,它可以根据当前的工作状况进行调整[又名工作条款说明

    (Statement of Work)]。第5章 敏捷反馈

    一步行动,胜过千万专家的意见。

    ——Bill Nye, The Science Guy 科普节目主持人

    在敏捷项目中,我们小步前进,不停地收集反馈,时刻矫正自己。但是,这

    些反馈都是从何而来呢?

    在上一章中,我们讨论了与用户一起紧密工作——从他们那里获得反馈,并

    且采取实际的行动。本章中,我们主要讨论如何从其他渠道获得反馈。按照

    Bill Nye的观点,实践是绝对必需的。我们会遵循这一原则,确保你明确知

    道项目的正确状态,而不是主观臆测。

    很多项目,都是因为程序代码失控而陷入困境。修复bug导致了更多的bug,从而又导致了更多的bug修复,成堆的测试卡片最后会把项目压垮。这时,我们需要的是经常的监督——频繁反馈以确保代码不会变坏,如果不会更

    好,至少能像昨天一样继续工作。在第78页,介绍如何让守护天使 来监督

    你的代码。

    但是,这也不能防止你设计的接口或API变得笨重和难用。这时,你就要先

    用它再实现它 (从第82页开始介绍)。

    当然,不是说一个单元测试在你的机器上能运行,就意味着它可以在其他机

    器上运行。从第87页开始,可以看到为什么不同环境,就有不同问题 。

    现在,你拥有了设计良好的API和干净的代码,就可以看看结果是否符合用

    户的期望了。你可以通过自动验收测试 来保证代码是正确的,并且一直都

    是正确的。我们从第90页开始谈论这个话题。

    人人都想清楚了解项目的进度状况,但又很容易误入歧途,要么是被一些难

    懂的指示器误导,要么就是错误迷信华丽的甘特图、PERT图或者日历工具。

    其实,你想要的是能度量真实的进度 ,我们会在第93页介绍它。

    尽管,我们已经谈论了在开发的时候,与用户一起工作并及时得到用户的反

    馈,但是在其他的比如产品发布之后的很长一段时间,你还是需要再倾听用

    户的声音 ,我们会在第96页详细解释。19 守护天使

    “你不必为单元测试花费那么多时间和精力。它只会拖延项目的进

    度。好歹,你也是一个不错的程序员——单元测试只会浪费时间,我

    们现在正处于关键时刻。”

    编写能产生反馈的代码

    Coding feedback

    代码在快速地变化。每当你手指敲击一下键盘,代码就会被改变。敏捷就是

    管理变化的,而且,代码可能是变化最频繁的东西。

    为了应对代码的变化,你需要持续获得代码健康状态的反馈:它是在做你期

    望的事情吗?最近一次修改有没有无意中破坏了什么功能?这时,你就带上

    守护天使,确保所有功能都能正常工作。要做到这样,就需要自动化单元测

    试。

    现在,一些开发者会对单元测试有意见:毕竟,有“测试”这个词在里面,毫无疑问这应该是其他人的工作。从现在开始,忘掉“测试”这个词。就把

    它看作是一个极好、编写能产生反馈的代码的技术。

    先回顾一下,在过去大部分开发者是如何工作的:你写了一小块代码,然后

    嵌入一些输出语句,来看一些关键变量的值。你也许是在调试器中或者基于

    一些桩(stub)程序来运行代码。你手工查看所有的运行结果,来修复发现

    的所有问题,然后扔掉那些桩代码,或者从调试器中退出,再去解决下一个

    问题。

    敏捷式的单元测试正是采取了相同、相似的过程,并且还让其更上一层楼。

    不用扔掉桩程序,你把它保存下来,还要让其可以自动化地持续运行。你编

    写代码来检查具体值,而不是手工检查那些感兴趣的变量。

    用代码来测试变量的具体值(以及跟踪运行了多少个测试),已经是非常普

    遍的做法。你可以选择一个标准的测试框架,来帮助你完成简单的编写和组

    织测试的工作,如Java的JUnit、C或.NET的NUnit、测试Web Service的

    HttpUnit,等等。实际上,对任何你可以想象到的环境和语言都有对应的单

    元测试框架,其中的大部分都可以从

    http:xprogramming.comsoftware.htm 上的列表中找到。

    清楚自己要测试的内容读者David Bock告诉了我们下面这个故事:

    “我最近在设计一个特大项目中的一个功能模块,把构建工具从Ant迁移

    到Maven。这是在产品中已使用的、没有任何问题的及经过良好测试的代

    码。我不停地工作,一直到深夜,一切都在控制之中。我修改了一部分构

    建过程,忽然得到了单元测试失败的警告。我花了很多时间,来查找为什

    么修改的代码会导致测试失败。最后我放弃了,回滚了修改的代码,但测

    试仍然失败。我开始研究测试代码,才发现失败的原因是,测试依赖一个

    计算次数的工具,而且它还返回一个日期实例,日期设置为第二天中午。

    我又看了看测试,发现它居然记下了测试执行的时间,并将其作为参数传

    递给另外一个测试。这个方法有个愚蠢的差一错误(off-by-one),如果

    你是在夜里11点到12点间调用这个方法,它真正的返回值仍然是当天中

    午,而不是明天。”

    从上面的故事中,我们学到了很重要的一课。

    确保测试是可重复的。使用当前的日期或者时间作为参数,会让测试

    依赖运行时间,使用你自己机器上的IP地址同样会让它依赖运行时的

    机器,等等。

    测试你的边界条件。11:59:59和0:00:00都是不错的日期测试边界条

    件。

    不要放过任何一个失败的测试。在前面的案例中,一个测试一直失败

    了,但是因为一段时间内每天都会有几十个测试失败,没有人会注意

    到这个伪随机失败。

    只要有了单元测试,就要让它们自动运行。也就是每次编译或者构建代码的

    时候,就运行一次测试。把单元测试的结果看作是和编译器一样——如果测

    试没有通过(或者没有测试),那就像编译没有通过一样糟糕。

    接下来就是在后台架设一个构建机器 ,不断获得最新版本的源代码,然后

    编译代码,并运行单元测试,如果有任何错误它会让你及时知道。

    结合本地单元测试,运行每个编译,构建机器不断编译和运行单元测试,这

    样你就拥有了一个守护天使。如果出现了问题,你会立刻知道,并且这是最

    容易修复(也是成本最低)的时候。

    一旦单元测试到位,采用这样的回归测试,你就可以随意重构代码。可以根

    据需要进行实验、重新设计或者重写代码:单元测试会确保你不会意外地破

    坏任何功能。这会让你心情舒畅,你不用每次写代码的时候都如履薄冰。单元测试是最受欢迎者的一种敏捷实践,有很多图书和其他资料可以帮你起

    步。如果你是一个新手,建议阅读《单元测试之道》(有Java[HT03]和C

    [HT04]版本)。如果要进一步了解测试的一些窍门,可以看一下JUnit

    Recipes [Rai04]。

    如果想要自动化地连接单元测试(和其他一些有用的东西),可以阅读《项

    目自动化之道》[Cla04]。尽管它主要是关于Java的,但也有类似的可以用

    于.NET环境或者其他环境的工具。

    如果你仍然在寻找开始单元测试的理由,下面有很多。

    单元测试能及时提供反馈 。你的代码会重复得到锻炼。但若修改或者

    重写了代码,测试用例就会检查你是否破坏了已有的功能。你可以快速

    得到反馈,并很容易地修复它们。

    单元测试让你的代码更加健壮 。测试帮助你全面思考代码的行为,帮

    你练习正面、反面以及异常情况。

    单元测试是有用的设计工具 。正如我们在实践20中谈论到的,单元测

    试有助于实现简单的、注重实效的设计。

    单元测试是让你自信的后台 。你测试代码,了解它在各种不同条件下

    的行为。这会让你在面对新的任务、时间紧迫的巨大压力之下,找到自

    信。

    单元测试是解决问题时的探测器 。单元测试就像是测试印制电路板的

    示波镜。当问题出现的时候,你可以快速地给代码发送一个脉冲信号。

    这为你提供了一个很自然的发现和解决问题的方法(见习惯35,第136

    页)。

    单元测试是可信的文档 。当你开始学习新API的时候,它的单元测试

    是最精确和可靠的文档。

    单元测试是学习工具 。在你开始学习新API的时候,可以为这个API写

    个单元测试,从而加深自己的理解。这些学习用的测试,不仅能帮助你

    理解API的行为,还能帮助你快速找到以后可能引入的、无法与现有代

    码兼容的变化。

    使用自动化的单元测试 。好的单元测试能够为你的代码问题提供及

    时的警报。如果没有到位的单元测试,不要进行任何设计和代码修改。切身感受

    你依赖于单元测试。如果代码没有测试,你会觉得很不舒服,就像是在高空

    作业没有系安全带一样。

    平衡的艺术

    单元测试是优质股,值得投资。但一些简单的属性访问方法或者价值不

    大的方法,是不值得花费时间进行测试的。

    人们不编写单元测试的很多借口都是因为代码中的设计缺陷。通常,抗

    议越强烈,就说明设计越糟糕。

    单元测试只有在达到一定测试覆盖率的时候,才能真正地发挥作用。你

    可以使用一些测试覆盖率工具,大致了解自己的单元测试的覆盖情况。

    不是测试越多质量就会越高,测试必须要有效。如果测试无法发现任何

    问题,也许它们就是没有测试对路。

    20 先用它再实现它

    “前进,先完成所有的库代码。后面会有大量时间看用户是如何思考

    的。现在只要把代码扔过墙去就可以了,我保证它没有问题。”

    很多成功的公司都是靠着“吃自己的狗食”活着。也就是说,如果要让你的

    产品尽可能地好,自己先要积极地使用它。

    幸运的是,我们不是在做狗食业务。但是,我们的业务是要创造出能调用的

    API和可以使用的接口。这就是说,你在说服其他人使用它之前,先得让自

    己切实地使用这些接口。事实上,在你刚做完设计但还没有完成后面的实现

    的时候,应该使用它。这个可行吗?

    编程之前,先写测试

    Write tests before writing code

    使用被称为TDD(Test Driven Development,测试驱动开发)的技术,你总

    是在有一个失败的单元测试后才开始编码。测试总是先编写。通常,测试失

    败要么是因为测试的方法不存在,要么是因为方法的逻辑还不足以让测试通

    过。先写测试,你就会站在代码用户的角度来思考,而不仅仅是一个单纯的实现

    者。这样做是有很大区别的,你会发现因为你自己要使用它们,所以能设计

    一个更有用、更一致的接口。

    除此之外,先写测试有助于消除过度复杂的设计,让你可以专注于真正需要

    完成 ......

您现在查看是摘要介绍页, 详见PDF附件(2602KB,171页)