在帮助客户实施敏捷的过程中,ThoughtWorkers常被问到一个问题:有没有一套标准的“敏捷模板”可供快速入门之用?

作为一种强调持续改进的方法学,自然不会有一套放诸四海而皆准的“标准流程”;但对于希望采用敏捷方法的组织和个人而言,若有一组普遍适用的最佳实践作为基础,便能少走许多弯路,以期事半功倍之效。

摆在你面前的,正是这样一本“敏捷入门手册”。

本迷你书从《ThoughtWorks文集》的13 篇文章精选5篇编撰成集。这几篇文章有一个共同点:它们介绍的是一些最根本、最易施行、又最能立竿见影的敏捷实践。藉由这几篇各自独立而又相互关联的文章,我们希望帮助读者从持续集成和测试入手,建立行之有效的项目健康保障体系,并掌握必要的面向对象编程和重构技能,从而切实提升软件质量,并为更进一步的改进打下坚实基础。

如果你喜欢本书,可以 购买原版《ThoughtWorks文集》

或在InfoQ中文站 免费下载这本书(PDF)

这本《 ThoughtWorks文集 》中译本面世之际,也正值“敏捷中国2009大会”召开在即。两者可谓相得益彰。

从2004年进入中国,ThoughtWorks见证和参与了中国敏捷社区的发展历程:从五年前的筚路蓝缕,到如今的欣欣向荣。更令人欣慰的是,在原则、价值观等“大问题”上,敏捷的实践者们已经基本达成共识,社区的话题更加趋于关注实践──这意味着敏捷社区正在步入成熟,将用他们的知识和技能为各自效力的企业创造更大的价值。

我们在这个时候把《ThoughtWorks文集》翻译出版,是希望为社区的发展再尽绵薄之力。作为敏捷方法的积极推动者,ThoughtWorks从多年、多个行业的实践中积累了丰富的经验。本书收录的13篇文章涵盖了编程技术、项目管理、持续集成、测试等方面内容,将带领读者了解ThoughtWorks在软件生命周期各个环节所推荐的工作方式。

比较难得的是,这本《文集》不仅由ThoughtWorks员工撰写,也由ThoughtWorks员工翻译。译者们或是与文章作者素有私交,或是在文章所论述的领域有所专擅,这也使得翻译的质量更有保障。感谢这些译者在工作之余的辛勤翻译,才使这本《文集》如期付梓。他们是:韩锴,胡振波,金明,李剑,乔梁,熊节,徐昊,张晓庆,郑晔。

一本薄薄的《文集》当然不可能解决所有问题,我们更希望它能够收到抛砖引玉的效果。希望ThoughtWorks的经验心得能对国内的敏捷实践者们有所启发,帮助他们做出更多创新,创造更大价值。最后,希望你阅读愉快。

郭晓
总经理,ThoughtWorks中国公司

书中的文章在技术深度、详细程度、新观点/新成果的数量等方面各有千秋,但它们有一个共同的特点:都密切关注实践。这群作者真正做到了思行合一,如此既有思想深度又立竿见影的好书我已多年未见了。

Stefan Tilkov. CEO, innoQ

有这样一家企业,当整个IT行业都对定制软件开发的难度望而却步时,他们敢于挑战这个难题。这本文集让我们得以管窥这家企业内部观点的多样性──这正是他们勇气和力量的源泉。

W. James Fischer. 前CTO/资深合伙人,Accenture

从CruiseControl等大获成功的开源项目,到博客和会议上分享的观点,你都能感受到ThoughtWorks的影响力。身处其外的我们不时会想象,这家公司内部究竟进行着怎样的讨论?这本书就是一个难得的机会,让读者们拉开幕布,参与到讨论之中──你会因此成为一个更出色的软件开发者。

Nathaniel T. Schutta. 作者/讲师/教师

软件开发很大程度上是一种团队活动,团队的领导者将决定最终产品的水准。成功的组织经常不会花时间来记录这些领导者的经验,于是其他人也无法学习这些经验。这本有趣的文集由ThoughtWorks的一组领导者共同编撰,透过这些文章我们能看到ThoughtWorks企业文化之一斑。

Dave Thomas. Bedarra Research Labs

软件开发中最了不起的洞见都出自那些为真实客户解决真实问题的人。然而除了在恒河沙数的博客中淘金之外,几乎没办法找到这些洞见。十年来,ThoughtWorkers解决了大量真实问题,所以当他们决定把自己的经验整理结集,这实在是一件令人欣喜的事。

Gregor Hohpe. 《Enterprise Integration Patterns》作者

这本文集精彩地论述了如何在如今的商业环境下恰当运用编程语言和工具来开发软件。这组作者都是软件世界里身经百战的老兵,他们的经验值得一看。

Terence Parr. ANTLR项目领导,San Francisco大学

ThoughtWorks 素来以其在软件开发上的经验与智慧闻名于世,这本文集出色地汇集了这些经验与智慧,使我们得以从中受益。这本书将被频繁引用,它将出现在每个项目组的书架上。

Jeff Brown. 北美运营总监,G2One

(摘自《 ThoughtWorks文集 》,第6章,“对象健身操”)

规则2. 拒绝else关键字

每个程序员都熟知if/else结构。几乎每种语言都支持if/esle。简单的条件判断对于任何人来说都不难理解。不过大多数程序员也见识过令人眩晕的层层嵌套的条件判断,以及连绵数页的case语句。更糟糕的是,在现有的判断条件上加一个新的分支通常是非常容易的,而将它重构为一个更好的方式的想法却罕有人去提及。条件判断结构通常还是重复代码的来源。例如,状态标识经常会带来这样的问题:

public static void endMe() {
    if (status == DONE) {
        doSomething();
    } else {
        // other code
    }
}

你有很多种方式重写这段代码,去掉else关键字。例如下面的代码:

public static void endMe() {
    if (status == DONE) {    
        doSomething();
        return;
    }
    // other code
}

public static Node head() {
    if (isAdvancing()) { return first; }
    else { return last; }
}

public static Node head() {
    return isAdvancing() ? first : last;
}

在上面的例子中,第二段代码由于使用了三元运算符,所以代码长度从四行压缩到了一行。需要小心的是,如果过度使用“提前返回”,代码的清晰度很快会降低。《设计模式》[GHJV95]一书中关于策略模式的部分里有一个实例,演示了如何使用多态避免根据状态进行分支选择的代码。如果这种根据状态进行分支选择的代码大量地重复,就应该考虑使用策略模式了。

面向对象编程语言给我们提供了一种更为强大的工具——多态。它能够处理更为复杂的条件判断。对于简单的条件判断,我们可以使用“卫语句”和“提前返回”替换它。而基于多态的设计则更容易阅读与维护,从而可以更清晰地表达代码的内在意图。但是,程序员要做出这样的转变并不是一帆风顺的。尤其是你的代码中可能早已充斥了else。所以,作为这个练习的一部分,你是不可以使用else的。在某些场景下可以使用 Null Object 模式,它会对你有所帮助。另外还有很多工具和技术都可以帮助你甩掉else。试一试,看你能提出多少种方案来?

(选自《 ThoughtWorks文集 》第14章)

性能需求采集的重要性经常被人们低估。在这一节里,我将尝试阐明几个重要问题:要度量什么?如何知道我们需要什么?以及如何得到确实有用(而非帮倒忙)的数据?

要度量什么?

最重要的性能度量点有两个:最大吞吐量,以及给定吞吐量下的响应时间。一个好的做法是:分别度量几种不同吞吐量下的响应时间,从中分析负载对响应时间的影响。如果响应的及时性非常重要,那么在确保满足响应时间要求的前提下所能达到的吞吐量可能就会明显低于最大吞吐量。你需要通过度量找出两项数据:当响应时间恰好可以接受时的吞吐量,以及达到预期吞吐量时的响应时间。伸缩性度量的关键则在于:随着数据规模、用户数量或者运行系统的硬件变化,起初得到的性能度量数据会发生怎样的变化。

可靠性的关键度量点是:当负载量高得超乎寻常,或者连续运行了很长时间以后,系统是否仍然正常工作。

如何设定目标?

要想知道系统需要达到怎样的吞吐量目标,你首先需要知道有多少用户会使用这个系统,以及他们的使用模式。用户会多频繁地使用某个功能?这个功能需要多快完成?

业务用户会知道这些问题的答案。你应该让他们明白,你会经常需要向他们咨询这方面的事。然后你应该建立一个良好的沟通流程,以确保信息的获取畅通无阻。

总而言之,你需要有一个可靠的流程与机制来获得所需的信息,使你及时获知支撑业务需求所需的性能指标。如果不经常去计算这些数据,就有可能最后发现你正在朝着已经过时的目标努力。

弄清当前需要负载的吞吐量之后,下一个需要考虑的就是响应时间。在结合UI考虑这个问题时,人们常会有钻牛角尖的想法:既然用户界面要在几秒钟之内响应,那么功能自然必须在更短的时间内完成。但事实并非如此。UI应该立即响应,告知用户:他们的请求已经得到处理;但实际的处理未必马上完成。在整个过程中,系统的其他部分应该照常工作。

响应时间的目标应该主要针对用户界面,并且数值越低越好。而且,不应该期望所有功能都能在同样的一段时间内完成。

如果对前面所说的还不明白,下面我将简单介绍一个采集性能需求的流程。

如何将性能测试融入日常开发流程?

理想情况下,项目组每周应该召开一次会议,确定当前的性能需求。参加这次会议的人应该包括项目经理、关注性能的客户、资深开发者、以及性能测试人员。如果某些性能需求明显无法达到或者完全不合理,开发者就能在第一时间指出。客户的参与是为了提供业务上的信息与知识,从而帮助判断需求的合理性。项目经理需要知道团队做了哪些决定,并提供一些方向性的指导。至于性能测试人员,他们显然应该在场,这样他们才知道需要测试什么。

接下来,你需要找到适当的讨论对象。开发团队需要从客户中找到一个联系人,与他一道决定性能需求,这样才能确保客户和开发者都清楚目标所在。不要把性能需求看作神圣不可侵犯之物,和所有需求一样,它们也应该是开发者与客户对话的起点,双方需要共同讨论决定最终的目标。

一旦需求确定下来,就能决定当需求得到满足时如何向客户展示,并对编写测试的工作进行评估和计划,就跟其他的任务一样。

程序员需要性能测试告诉他们什么?

开发者的需求有很多种,但背后的驱动力总是一致的:如果某段代码需要返工,他们就需要更多的信息来了解当时的情况。这些信息可能来自代码检查工具,也可能来自线程转储,甚至来自日志。他们可能需要知道数据库的忙碌程度,或是负载达到峰值时网络的忙碌程度。

试图预先回答所有这些问题可能并不划算,因为这会需要很大工作量。但我们可以做的是:当问题出现时,弄清哪些信息会有助于开发者解决问题,然后把获取这些信息的任务加到你的任务列表上,并告知客户。此时你就可以判断应该如何进行这些测试:是从此刻开始持续测试,还是只针对眼下的特定问题做一次性测试。

如果开发者的需求以这种方式在会议上提出的,那么所有人都将知道这些需求的存在。客户可以为这些需求排优先级,可以把它们纳入项目计划。最终性能测试将满足各方的需求:它让客户对正在开发的软件保持信心,它也能帮助开发者找到并解决性能问题。

找不到关注性能的客户怎么办?

如果找不到一个关注性能需求的客户,就会有几方面的风险。首先,正在开发的软件可能不符合业务要求,项目可能彻底失败。其次,不管最终的产品如何,客户都可能说它不符合要求,因为他们感觉开发团队没有征求他们的意见。第三,这可能会在团队内部造成紧张气氛,开发团队会觉得自己在被迫做不必要的工作,因为需求不是来自客户──不管项目经理的担心是否正确,这种想法都有可能出现,并导致必要的工作没有被完成,或是相反,开发者们浪费时间去做不必要的工作。

如果客户不懂技术又非要坚持不可能的需求该怎么办?

这种可能性总是存在:客户希望产品的性能达到某个水平,而达到这个水平是不可能或者不经济的。这时你就需要提出一些中肯的问题,把对话引导到真实的业务需求上来,从而打消客户不切实际的要求。

如果客户的要求是关于吞吐量的,可以考虑的问题有:每个工作日处理多少事务?这些事务的时间分布如何?是平均分布还是有明显的高峰期?每个周五下午会有集中访问吗?或者峰值的出现没有特别的模式可循?

关于响应时间,可以考虑的问题有:用户界面的响应时间会对系统的处理能力造成什么影响?能不能把界面与实际的计算操作分离?比如说,可能有这样一种场景:用户输入一些数据,然后进行较长时间的数据处理。此时用户不希望一直等到处理完成,而是希望立即输入下一段数据。所以这时合理的期望不是在一秒钟内完成数据处理,而是将用户界面与数据处理分离,让系统在后台处理前一段数据,同时让用户在界面上输入更多的数据。

以这种讨论方式,我们就能让开发者和客户共同寻找一个对业务价值有意义的性能水平,并且分清什么是当务之急、什么是锦上添花。我们都曾遇到这样的情况:在项目的现有条件下,客户急切希望的某个性能目标不可能达到、或是需要付出高昂的代价。如果相关的分析能尽早开展,客户就有可能在更早的时候做出决定,从而使这些目标成为可能。

如果客户期望的目标不能达成,他们会对最终交付的系统感到失望,哪怕系统其实足以满足业务需求。上述这些讨论有两方面的作用:不仅让开发团队了解客户的真实需求,而且让客户自己也有一个清晰的目标。这样一来,只要系统达到了双方认可的目标,客户就会感到满意。有这些讨论作为基础,客户就不太会坚持不切实际的期望;如果他们仍然感到失望,至少那也是出于合理的原因。

何不让业务分析师一并采集这些需求?

采集性能需求时不一定需要业务分析师在场,原因有几点:首先,此时功能需求的采集应该已经完成了;其次,即使业务分析师在场,开发者还是不能缺席,因为分析性能问题需要获得哪些信息只有开发者才清楚,也只有他们才能判断获得这些信息的途径和难度。性能测试人员应该提出前面介绍的这些问题,以此推动讨论进行,他们也能够判断每个需求是否容易测试。所以,当这些人坐在一起讨论时,业务分析师大可以把时间花在其他更有价值的地方。

小结

需求采集是为了让所有人都清楚:最终交付的产品需要有怎样的性能才能支撑业务目标。之所以要让客户参与,是因为他们最了解自己的业务,这样才能确保采集到的需求足够准确。而且通过讨论也能帮助客户清晰自己对性能的需求,从而有效管理他们对系统的期望。

(摘自《 ThoughtWorks文集 》,第1章。)

作为一家公司,ThoughtWorks汇聚了一批热情洋溢、积极主动、才智过人的员工,他们为客户提供定制软件开发以及切合实际的咨询服务。如果你问一个ThoughtWorker,这家公司最让他最喜欢的是什么,他很有可能会告诉你:正是那些朝夕相处、并肩工作、彼此学习的同事们。在这个群体里融合了技术极客、管理者、分析师、程序员、测试员和行政人员,他们有着不同的种族、文化和教育背景。这种背景和视角的多样性,再加上坚持正确观点的热情,引发了很多活跃的讨论。

如今ThoughtWorks拥有近千名聪明而有见地的员工,在全球6个国家设有分支机构,组织内部几乎没有任何层级,并且一以贯之地坚持信息透明。可以说,我们已经创造了一家成功的企业。但我们对“成功”的定义远不止于此:一家企业的成功不仅意味着让客户满意,还应该对整个行业乃至整个社会产生正面的影响。我们有着更高的目标。

在博客世界里,在技术大会的会场中,在互联网上,在书架上,我们都能听到ThoughtWorker的声音。在不断追求卓越的过程中,我们会近乎冷酷地剖析自己曾做过的事、以及做这些事的方法,以寻求改进之道──在这方面我们永无饗足。在上下求索之中,一旦学到了什么知识,我们就希望与他人共享。

...

这本文集所收录的文章虽然各自成篇,彼此之间却有着千丝万缕的联系:它们共同展示了一片布满迷雾的IT丛林,以及一条条或显而易见、或出人意表的林间小径。文章选题跨度之宽、解决问题的办法差异之大,恰能反映众位作者所在的这个组织为各种思想的萌生营造了一个健康的环境。阅读这本文集恰如管中窥豹,让我迫不及待地想看到这些才华横溢的同事们为整个行业、整个社会创造更多。