0%

【书摘】软件项目成功之道

《程序员修炼之道》为我们介绍了软件开发人员个人的技术和能力,这本书则是面向软件开发团队。如果你认识的同在软件领域打拼的某个人工作上不顺心,可以让他读一读这本书。


教条并不意味着没有思想,而是思想的终结。 ——吉尔伯特·基思·切斯特顿 (1874—1936)

事实上,开发软件根本没有所谓“绝对正确的方法”。倒是有很多错误的方法,不过没有哪一种方法、观点、哲学或工具能“以不变应万变”,在所有时间、所有场合对所有项目和所有人都适用。软件是人创建的,不会有两个人完全一样。

不论是对你自己还是对你的职业发展,最明智的一项投资就是让你身边有一些“合适”的人——他们会成为你能找到的最好的资源。这些人可能已经做过你打算做或者想学着做的事情。如果你想做一些事情,最好找到那些做过这些工作的人,或者至少找到一些真正睿智的人,能够告诉你该怎样完成这些工作。尽可能和他们多待些时间,通过相互帮助来向他们学习。与这些高水平的人相处,会让你学到很多东西,不论你的工作是什么,这都会让你表现得更出色。

一个月读一本书应该不会太费劲。但是不要就此止步,接下来可以学习一种新的编程语言,或者研究一个不同的开发过程。在学习和读书时,要想办法把这些新思想应用到当前的工作中。这样一来,你不仅能帮助你的公司提升,更重要的是,还可以让你自己得到提高。

要让自己开放地接受新思想。不要闭塞,应当想办法把这些新思想应用到你现在的工作中。也许你会放弃,并声称某种新思想不适用,这样做当然更轻松,不过我们的目标是学习采用另外一种不同的方式考虑问题。要打破条条框框(或者至少建一个更大的框框)。多学习掌握一些看似不太相关的概念和思想。

要让自己开放地接受新思想。不要闭塞,应当想办法把这些新思想应用到你现在的工作中。也许你会放弃,并声称某种新思想不适用,这样做当然更轻松,不过我们的目标是学习采用另外一种不同的方式考虑问题。要打破条条框框(或者至少建一个更大的框框)。多学习掌握一些看似不太相关的概念和思想。

通过对环境和过程进行分析和评判,你可以找出弱点。也许这会帮助你对这个项目或者下一个项目做出改进。但同时你也练习了一种新的思维方式,不论在哪里工作这对你都会有好处。

如果我们坚持不懈,那么,优秀就不再是一种行为,而成为一个习惯。 ——亚里士多德

能够生产出一个或一些不错的产品算不上优秀。优秀体现在我们每天所做的点点滴滴,也就是我们的习惯。一流的产品只不过是好习惯的副产品。

优秀体现在我们每天所做的点点滴滴,也就是我们的习惯。一流的产品只不过是好习惯的副产品。

要有意识地搜寻好习惯,并把它们加到你的日常生活当中。

要有意识地搜寻好习惯,并把它们加到你的日常生活当中。

可以做个小实验。找一种要研究的开发方法,挑选一个看上去对你来说不错的习惯(而且这个习惯可以单独运用)。实际使用一个星期。如果你喜欢,而且看来有好处,就继续用上一个月。不断实践这个新习惯,把它变成为你生活模式中很自然的一部分,然后再选择另一个新习惯重新开始这个过程。

“我们的每一天怎样度过,一生就会怎样度过”。既然如此,就必须仔细考虑如何度过我们的每一天。

技巧1:选择习惯 不要偶然地养成某些习惯,要有意识地主动选择习惯。

敏捷(agility)是指软件团队能够很快适应不断变化的条件的能力。这有时意味着要重新设计以适应变化的需求,有时则意味着快速应对新的bug或迅速采用新的技术。总之,敏捷团队更关注结果而不是形式。

“我们正在提出更好的软件开发方法,我们自己在使用,同时还帮助其他人使用。通过这些工作,我们意识到: 个体及互动比过程与工具更有价值可用的软件比冗长的文档更有价值 客户协作比合同谈判更有价值 对变化的响应比遵循计划更有价值也就是说,我们认可上述右边事项的价值,但我们更加重视左边的事项。”
只需记住一个基本原则:在你准备好之前,要与其他人隔离,使他们不会受到你的工作的影响。基于此,我们把这种方法称为沙箱开发(sandboxdevelopment):每个开发人员都有自己的沙箱,可以在沙箱中尽情尝试,而不会干扰其他开发人员。

只需记住一个基本原则:在你准备好之前,要与其他人隔离,使他们不会受到你的工作的影响。基于此,我们把这种方法称为沙箱开发(sandboxdevelopment):每个开发人员都有自己的沙箱,可以在沙箱中尽情尝试,而不会干扰其他开发人员。

你自己的开发机器应当特别针对你,可以提高你的生产效率,而不应对全局构建过程有任何贡献——别人做任何事情不必直接依赖你的机器。

技巧2:留在沙箱里

技巧3:如果需要就将其签入

你的源代码管理系统一定要包含构建、部署和运行产品所需的一切。如果做不到这一点,实际上就是为了节省磁盘空间而拿开发项目的长期发展在冒险。

如果你的项目没有使用任何源代码控制措施,那么现在就要把它作为最首要的事情来办。

技巧4:从第一天起就使用脚本构建

要保证在工作室的每一个工作站上都能以完全相同的方式构建产品。

技巧5:任何机器都可以作为构建机

必要时,只要使用相同的签入脚本,任何开发人员的机器都可以作为构建机。我们的目标是任何有构建条件的机器都应当能完成与构建机完全相同的构建(除了时间戳、IP地址或机器名等有所不同之外)。

适当地使用你的手动构建系统,你就可以构建整个产品并保证: 只使用一个命令就可以完成构建; 会根据源代码管理系统(SCM)来构建;可以在任何团队成员的工作站上构建; 没有任何外部环境要求(如特定的网盘)。

警告信号 构建中包括手动步骤。 必须修改你的构建脚本才能在不同的机器上运行。 只有少数团队成员知道如何编辑构建脚本。

不需要人照管的构建就是自动构建。不过,在实现一个自动构建之前,必须有一个只需一个命令就能运行的手动构建系统。如果没有这样一个手动构建系统,可以再返回去学习实践3。你不能对一个不存在的过程实现自动化。

技巧6:持续构建

bug回归(bug Regression)是指你原先已经修正的一个问题又重新潜入代码。如果你在问题修正之后意外地将不好的代码又放回到SCM,或者重新引入同样的错误,就会发生这个问题。bug回归确实是一个很让人头疼的问题。如果为修正的每一个bug编写一个测试,并在CI系统中运行,在签入有问题的代码时系统就会捕获到bug回归。这种策略可以有效地防止bug回归。

技巧7:持续测试

不过,跟踪问题不只是描述问题本身。跟踪并有效地交流问题的细节不是那么容易就能做到的。你需要知道以下信息: 哪个版本的产品存在这个问题?哪个客户遇到这个问题? 这个问题有多严重? 这个问题可以在公司内部再生吗?(由谁再生?如果你自己不能再生这个问题,就可以向他们寻求帮助。)客户的环境是怎样的(使用什么操作系统、数据库,等等)? 这个问题最早出现在产品的哪个版本中? 这个问题在产品的哪个版本中得到修正? 谁修正了这个问题?由谁验证了该修正?

跟踪系统不仅可以为产品生成已知问题列表,还可以为开发人员生成任务列表。如果没有人修正问题,就没有必要浪费时间来查找问题。

技巧8:避免集体失忆

问题跟踪系统就是一个簿记明细。你需要它记录你做的工作,修正了的和没有修正的问题,以及计划修正的问题。白板、索引卡或活页本或许可以应付几个月,但不是长久之计,而且肯定无法适应企业的具体要求。

如果你现在还没有一个问题跟踪系统,那千万不能再等了。不要等到迁移完每一个遇到的问题之后才考虑迁移到这个系统。这是一个宏伟的目标,但是不要干等着手动系统“干净”了才使用自动系统。只要可以,就立即开始使用。把新问题输入到你的新系统中,过一段时间就会积累大量的信息,成为至关重要的资源。

自动化测试框架(testing harness)是用来创建和运行测试的工具或软件工具包。除了自动化测试,还可以手工编写独立的测试(或者更简单,根本没有测试)。 如果你的测试不是自动的,就无法用一个脚本来运行这些测试。你需要一个人来运行你的测试套件,这就需要花费时间和金钱。而且人们每次做事情总会稍有差别。交互式测试对于测试工作很有意义,好的自动测试套件也一样。

好的测试套件就像优秀的测试人员或开发人员一样,价值非比寻常。它有助于你的产品有上佳表现,能够很快捕获问题,向开发人员迅速做出有关产品状态的反馈。

技巧9:演练产品——自动测试

使用一个“现成的”测试框架有很多好处。例如,与你自己创建的测试框架相比,它的特性集会更为全面。如果你选择一个知名的框架,还可以找到很多辅助产品。

技巧10:使用通用、灵活的自动化测试框架

测试的类型有很多种,它们识别问题的类型也各不相同。

  • 单元测试(unit test)用来测试单个的类或对象。这些测试是独立的,通常不需要运行其他类或对象。单元测试的唯一作用就是验证一个代码单元中逻辑操作的正确性。
  • 功能测试(functional test)用来测试整个产品的操作或功能是否正确。这些测试可以针对整个产品,也可以针对产品中主要的子系统。功能测试会测试系统中的多个对象。
  • 性能测试(performance test)测量产品或一个关键子系统的运行速度。如果没有这些测试,就无法知道一个代码变更对产品响应时间的影响(除非你很擅长掐秒表)。
  • 负载测试(load test) 模拟产品在很大负载情况下的表现,这些负载可能来自大量客户,也可能来自一组任务繁重的用户,或者二者都有。同样,如果没有这种类型的测试,你将无法客观地说明代码基是得到改进还是有所退步。
  • 烟雾测试(smoke test) 是一种轻量级的测试,必须经过仔细编写才能测试产品的关键部分。它们运行得很快,但仍然能测试产品的重要部分。其基本思想是运行产品来看它是否“冒烟”,也就是说调用基本功能时是否会失败。烟雾测试非常适合与CI系统联用(见实践4),因为它的速度很快。在产品的生命周期中,经常运行的烟雾测试可能会循环。烟雾测试套件主要针对活动的开发区域或已知的bug。
  • 集成测试(integration test) 查看产品线的各个部分如何集成在一起。这可能涵盖多个产品,有时是你的产品,有时还有你使用的第三方产品。例如,产品使用的各种数据库就可以作为集成测试的一部分来测试。这些测试往往会超越产品边界。集成测试通常用来验证产品所依赖的组件的新版本,如数据库。如果你喜欢的数据库推出了一个新版本,你肯定想知道产品能否在这个新版本上运行。一组集成测试可以从功能一直测试到数据库,这组测试将为你回答有关功能的问题,还会让你迅速了解这些新组件的性能。
  • 模拟客户测试(mock client testing) 用来从客户的角度创建测试。模拟客户测试设法为产品展现常见的使用场景,确保产品满足最低功能要求。这种测试绝对能够在保证基本测试覆盖率的前提下,涵盖最常用的代码路径。

一个好的测试框架会对开发工作产生难以置信的影响。如果有效地使用,这会是一个强大的工具。一定要投入必要的时间找到一个适合你的具体环境的框架,然后学习如何有效地加以使用。

一定要保证你的工具使用一种开放的格式,如XML或纯文本。如果一个工具采用开放的格式读写,它就能与任何其他使用开放格式的工具“交流”(假设语义匹配,或者经过转换后能够匹配)。利用这种适应性,就可以把多个不同的工具串连在一起,构成一个完整的端到端系统。利用似乎毫无关联的工具之间的这种交互,可以实现让人难以置信的协同工作。

一定要保证你的工具使用一种开放的格式,如XML或纯文本。如果一个工具采用开放的格式读写,它就能与任何其他使用开放格式的工具“交流”(假设语义匹配,或者经过转换后能够匹配)。利用这种适应性,就可以把多个不同的工具串连在一起,构成一个完整的端到端系统。利用似乎毫无关联的工具之间的这种交互,可以实现让人难以置信的协同工作。

技巧11:工欲善其事,必先利其器

技巧12:使用开放格式集成工具

不要用一种只适合个体的小环境技术(niche)或非核心技术来编写产品周期中的关键部分(如构建系统),特别是那种只有一个开发人员了解的技术。应当使用工作室里任何人都能配置和维护的技术。大杂烩式的科技游乐场(technology playground)固然很好,对于专业开发是必要的,但是在这里并不适用。试验应当避开关键路径。

创建一个关键技术(如构建系统)时千万不要把它变成一个技术试验。要使用专门的构建工具来创建你的构建,而不是采用某个团队成员想学习的一种很酷的新技术。有很多不太重要的领域可以供你学习新技术。不要创建只能在一个机器上运行的自动化工具。不要由于硬编码而依赖网盘。把你需要的所有信息都放在SCM系统中,这样一来,网盘就变得不重要了。

技巧13:使用熟悉的关键路径技术

这里介绍的技术是我们认为最有用的一些技术,具体包括: 任务清单(list) 技术领导人(tech lead) 每日例会(daily meeting)代码审查(code review) 代码变更通知(code change notification)

很多情况下我们会使用一个计划任务清单(to-do list)来跟踪我们的工作。任务清单(list)可以规范计划任务的概念,从而可以在团队环境中使用。

可以利用任务清单来安排你每天和每周的日程。利用任务清单可以安排你的工作顺序,还可以安排整个团队的工作顺序。(有点分形的意思!)如果你陷入困境、头昏脑胀、四处救火,可以回过头来查看任务清单,重新把握重点。如果你被一个极其棘手的问题卡住,需要暂时回避这个问题,任务清单会告诉你先做哪些容易的事项。这样可以确保你总在处理最重要的任务,而不是表面上最显眼的任务。

任务清单还会为团队提供充分的敏捷性。它确保你已经根据需要将产品分解为特性,并将特性分解为列表项,从而保证你已经提前做了一些基本的设计工作。另外,由于产品已经分解为特性,所以可以根据需要去除或者增加特性。

如果任务清单包含很多深入的工作,说明你已经提前考虑并对后面的步骤做了计划。

技巧14:按照任务清单工作

如果一项任务无法转换为可测量的目标,就把它设置为最低的优先级,先处理更高优先级的任务。如果这项任务的出发点是好的,将它完全删除可能是个错误,把它分解为可测量的任务就可以了。

如果一项任务无法转换为可测量的目标,就把它设置为最低的优先级,先处理更高优先级的任务。如果这项任务的出发点是好的,将它完全删除可能是个错误,把它分解为可测量的任务就可以了。

代码冻结(code freeze)就是代码基停止改变。在开发周期中,代码如水般流动,会不断变化。不过,代码冻结之后,改变会停止。代码冻结后只能做重大的bug修正,增加特性或修正不太重要的bug都是不允许的。

技术领导人对你的软件项目既要监督还要承担技术方面的责任。有了技术领导人,就可以把经理解放出来处理行政事务,而把技术方面的问题交给更胜任的人。经理可能身兼技术领导人的角色,不过这没有必要,很多情况下也不是一个好主意。如果你的经理缺乏必要的专业技术能力,或者你的团队在完成多个项目,最好有一个单独的技术领导人。

技术领导人在这两个领域都要涉足。他们必须与开发团队合作,了解开发团队;另外还要与管理层、客户和其他技术领导人会面来交流你的团队在做些什么。技术领导人的位置很特别,他们花时间与产品的客户见面,并了解他们的需求,来确定你的产品应当做什么

技术领导人在这两个领域都要涉足。他们必须与开发团队合作,了解开发团队;另外还要与管理层、客户和其他技术领导人会面来交流你的团队在做些什么。技术领导人的位置很特别,他们花时间与产品的客户见面,并了解他们的需求,来确定你的产品应当做什么。

技术领导人需要完成以下工作:

  • 确保团队的工作优先级与客户的需要一致;
  • 确保将团队的工作适当地展示给管理层;
  • 将团队与不懂技术的管理层隔离;
  • 为不懂技术的干系人解释技术问题;
  • 让开发团队了解非技术问题。

技术领导人的职责 技术领导人有很多重要的职责领域,不同公司和团队组织中可能有所不同。以下是技术领导人最起码的职责:

  • 为团队成员设定方向;
  • 管理项目的特性列表;
  • 为项目的特性确定优先级;
  • 隔离你的团队,使他们不受外部干扰。

1.为团队成员设定方向 技术领导人就是团队的导师,要设定方向和优先级。技术领导人要与每个团队成员合作来创建和维护个人任务清单(见实践10)。技术领导人要了解团队成员的进度、遇到的问题以及估计的完成日期,并利用这些来建立项目健康度的全局视图,同时跟踪进展。这会成为一个联络点,可以提供一种快捷的方式,允许任何人了解准确的项目状态更新情况。

2.管理项目的特性列表 技术领导人相当于项目特性列表的主要保管人。所有特性请求都要经过技术领导人的筛选而不是直接统统交给开发人员。技术领导人会管理所有特性变更。

3.为每个特性指定优先级 要为你的新列表设置一个顺序。如果没有一个既定的顺序,每个人可能都只会选择有意思的特性,而忽视那些真正必要的特性。要为各个特性安排正确的优先级,这几乎与在任务列表中增加适当的特性同样重要!幸运的是,这个问题可以交给你的技术领导人来完成。技术领导人必须与项目干系人合作来设置特性优先级。

优先级与数字相关联,第1优先级就是最高优先级,第5优先级则是最低优先级。当然,你可以根据自己的需要调整这些数字。我们目前就使用1到5,不过在有些情况下,我们也用过1到10。重要的是顺序,而不是数字范围。

  • 第1优先级:必要 这些是必须包含的特性,否则就不能交付产品。
  • 第2优先级:非常重要 即使不完成这些特性也可以交付产品,但是你可能不会这么做。
  • 第3优先级:可有可无 如果有时间,你可以完成这些特性,不过绝对不要因为这些工作延误交付日期。
  • 第4优先级:精雕细琢 这些特性可以为产品增加一种真正完成的感觉。
  • 第5优先级:无用 如果你有时间增加“无用”特性,说明你的进度超前而且没有超出预算。

4.隔离团队免受外部干扰 你正在做一个复杂的项目,一早上都“状态不错”,取得了不错的进展。就在这时销售部的一个人跑进来,问了一个关于下一版本的问题,把你的思路完全打乱了。光是看到这种情形就让你很恼火,是不是?不只是你,所有人都一样:如果不被打断,工作就会更有成效。实际上,研究人员指出,一个工作日中多达40%的时间都可能因为中断而浪费。这就像每天工作不到5个小时就下班回家!科学家们给这种现象起了个名字,叫做认知超负荷(cognitive overload)。了解到这一点,技术领导人就必须尽最大努力保证团队的工作不受干扰。对此,一种很好的做法就是把技术领导人作为开发人员的联络点。一定要让技术领导人来缓冲这些干扰,不论它来自IT人员还是干系人。

每个项目都应该有一个好的技术领导人,而且每个开发人员都应该渴望成为一个好的技术领导人,起码要有一次这样的经历。即使你从来没有担任过这个角色,掌握这些技能也会让你在开发团队乃至整个公司里变得举足轻重。大多数一流的技术精英在自己的职业生涯中都起码做过一次技术领导人。

技巧15:要有一个技术领导人

作为团队的技术领导人,你应该能顺利地回答以下问题: 你知道团队的每一个成员都在做什么吗?你能不能在5分钟内生成一个关于项目状态的总结?产品接下来要实现的5到10个特性是什么?你能不能很容易地列出产品中优先级最高的缺陷?你为团队成员解决的最近问题是什么?如果一个团队成员需要解决一个重要问题,他会来向你求助吗?

技巧16:通过每日例会频繁进行航向修正

如何起步 如果你以前从来没有开过每日例会,那可真够呛!下面是启动每日例会的几点想法。

  • 一定要让每一个人都知道模式(你希望哪些问题得到回答)。
  • 每个人都必须回答问题。没有人能跳过,无一例外。
  • 开始时,在时间限制上可以宽松一点。最开始会有大量新信息交换,所以必须允许沟通自由顺畅地进行。
  • 会议要在每天相同时间相同地点召开。让每日例会成为一种习惯,而不是勉强坚持的例行公事。
  • 把每日例会时讨论的话题发布在网页或plog上。
  • 挑选一个人开始会议,然后顺时针(或逆时针)轮流发言。一个人讲完后随机选择另一个团队成员发言比较容易让大家心理紧张。

如果你知道有别人在查看你的代码,并要求你对代码负责,你就会写出更好的代码。这不是开发人员独有的问题,而是人的天性。代码审查可以确保至少有另外一个人检查你的工作。你很清楚:这种问责制之下,你不能在代码中走捷径。

大量研究表明,代码审查在检测代码缺陷(bug)时非常有效。实际上,这是查找bug的头号技术。再没有比这更好的技术了。如果你没有坚持不懈地做代码审查,最后发现的问题可能会让你大惊失色。

代码审查为经验丰富的开发人员提供了一个绝好的机会,可以借此向缺乏经验的程序员传授编码风格和设计技术方面的经验。除了明确一些小的技术细节(如括号放在哪里),代码审查使有经验的老手有机会向新手们建议为什么应采用另一个数据结构,或者指出出现了一个模式。通常,在这些会话中审查人员会发现重复的代码或功能,可以移至公共基类或工具类中。这样在代码签入到源代码管理系统之前可以得到重构。

学习模式的另一个理由是,这可以帮助你解决以前从未见过的问题。通过阅读和讨论不同模式,你将学会如何解决很多常见问题。关键并不是你是否会遇到大多数模式,而在于当你遇到这些模式时能不能明确识别出来。你知道如何干净利落地解决这个模式表示的问题吗?还是跌跌撞撞做过多次代码迭代后才找到一个可接受的解决方案?

“重构是一种纪律性的技术,可以调整既有代码的结构,修改它的内部结构而不会改变其外部行为。其核心是一系列小的保持行为不变的转换。每个转换(称为一个重构)做的很少,但是一系列转换就会产生显著的结构调整。由于每个重构很小,所以不太可能出错。系统在各个小的重构之后依然能够正常工作,从而降低系统在重构期间被严重破坏的可能性

与软件开发有关的很多工作都是脑力劳动——一个问题会一直在我们脑海里盘旋,直到最终解决。如果你正处在需要全神贯注的情况下,让别人稍后再来并不是侮辱他。

技巧17:可以说“以后再来”

这些简短的代码审查可以促进知识技能的传授,而不存在正式培训的开销。要与不同的开发人员共同完成代码审查,这样一来,你就会得到不同开发人员的经验和专业技能,让你大有收获。各个审查人员可能会提出不同的方法来解决同一个问题。有的方法好一些,有的可能欠佳,不过都各有千秋。

技巧18:经常审查所有代码

代码审查是非常棒的工具!一旦养成习惯,你就会奇怪没有代码审查怎么可能写出高超的代码。可以使用以下的技巧作为起步。 要让每一个人都了解你计划做哪种类型的代码审查。要频繁地审查,而且每次只审查较小的代码块。不要等到几周后,已经积累了数百甚至上千行代码变更之后才做审查。不要让你的团队陷入MAD审查!在前几周或几个月里,要让一位高级团队成员参与每一个代码审查。这是共享知识的好办法,而且可以使审查有一个可靠的基础。确保代码审查是轻量级的。宁可审查太少代码,也不要太多。完成两个重叠的审查比完成一个较大的审查更好。要引入一个代码变更通知系统(见实践14“发送代码变更通知”)。这是对代码审查的一个很好的补充,而且有助于提醒忘记申请审查的团队成员。确保在要求所有团队成员参加之前得到管理层的认可。

“信息辐射器会在一个任何路人都可以看到的地方显示信息。有了信息辐射器,路人不需要再问问题,信息会在他们经过时显示给他们。“一个好的信息辐射器有两个特征至关重要。第一个是信息要随时间改变,这才值得人们花时间查看显示。另一个是查看显示所需的能量要很少。”

不论采用哪种方法实现,每次代码签入到源代码管理系统时,都应当将变更通知发送给你的团队。通知邮件应当包括以下内容:

  • 审查人员的名字;
  • 代码变更或补充的目的(例如,你修正了哪个bug,或者增加了哪个特性);
  • 新代码与老代码之间的差别(任何主流的源代码管理系统都会为你生成这个报告)。如果你完全
  • 重写了一个相当大的代码块,那么只列出差别就会毫无意义(因为二者差别过大),此时只需包括新代码。这一点同样适用于新文件。

代码通知是在团队成员之间培养责任感的一种简便易行的方法,有助于找出特立独行的开发人员,他们不愿做代码审查,或者增加的代码并不对应任务清单中的某个bug或特性。

《程序员修炼之道》提醒我们要有一种为自己的工作而自豪的工作态度。不论是设计软件解决方案还是盖教堂,都应该这样工作,就好像你处理的每一件工作都要在明亮的灯光下经过同事和客户的仔细检查一样。

下面对这一章讨论的技术做一个总结。可以把它复制下来,贴到墙上,并按章行事。很快你就会奇怪,使用这些技术之前你怎么会那么做事。 任务清单 – 可以公开获得 – 已经指定优先级 – 有一个估计时间表 – 活跃 技术领导人 – 管理项目的特性列表 – 跟踪开发人员当前的任务和状态 – 帮助指定各个特性的优先级 – 隔离团队免受外部干扰 每日例会 – 保证简短 – 力求具体 – 列出问题,但不要解决 代码审查 – 只审查少量代码 – 一两个审查人员 – 经常审查 – 不经审查不能发布代码 代码变更通知 – 用电子邮件发送并发布通知 – 列出审查人员的名字 – 列出代码变更或增补的目的 – 包含代码差异部分,如果篇幅允许还可以包含文件本身

曳光弹开发(Tracer Bullet Development,TBD)对软件项目也有同样的作用:采用这种方法,项目一开始你就能看到走向,它可以帮助你从项目早期开始就连续瞄准目标。TBD是我们见过的开发软件最有效的一种方法——这种方法非常易于使用,而且功能极其强大。

过程就是一组步骤,这些步骤连接在一起就能采用一种可重复的方式构建产品。利用过程,可以多次用同样的方式、同样的工具构建产品,这样一来,开发工作不再是一个碰运气的赌博,而变得可以重复并值得信赖。销售和市场团队向你提出产品想法后,你的团队每次都能交付产品。也许不能按销售部门理想的时间表来交付,但是公司完全可以相信:你肯定能在承诺的时间范围内交付你承诺的产品。

技巧19:目标是软件,而不是遵从过程

TBD并不试图改变你的工作方式,它只是“包裹”已有的工作方式,并力求做到不侵犯你的其他实践,与它们无缝地共存。这也是最小的可用过程之一,非常容易使用。

明确项目的主要部分,把产品分为相关功能块。例如,可能有名为客户(client)、Web服务器(Web server)和数据库层(database layer)等的块。 定义这些块需要交换哪些信息,并用方法签名记录这些信息。这些层间的交互称为接口。不要奢望前几次就能建立完美的接口。把各个块交给不同的开发人员、开发人员团队或者你的不同大脑分区(如果你单独工作)。

只需编写让一切看起来能正常工作的代码。可以把这想成是模拟对象的一个完整应用。每一层看起来在验证用户或获取数据(或者在完成你的应用需要做的任何工作),不过实际上每一层都是“桩”,只是在返回存储数据。 有了这个“瘦”的骨骼框架,接下来就可以开始在各个块中填入实际的逻辑代码。最后,也是最重要的,要记住在整个项目过程中接口会改变和发展。你的第一发子弹往往会漏掉目标,所以要灵活,适当调整你的瞄准点。其他团队要求新接口或者要修改现有的接口时,你就应当做出修改。毕竟,这是软件。

第一步是明确你的应用可以划分为哪些层(对象)。客户、服务器和数据库就是这种对象的很好的例子。当心不要定义底层对象。在客户服务器应用与基于API的数据库中,对“底层”的定义显然会大不相同。要确保你定义的每个系统对象都可以独立存在。如果创建了一个与系统其他部分之间有清晰界限的对象,这就可以是一个系统对象。稍后我们将看一个例子。

设计技巧 下面的技巧可以让你的接口设计会议顺利进行。

  • 总有一个人主持会议。这个人拥有发言权,任何人讲话之前必须经他“许可”。让某个人主持会议有助于避免会议变成一场争吵。
  • 整个会议中应在白板上记录要点。由于信息写在白板上,每个人都能立即看到大家认可的方法签名。如果在纸上记录,肯定有人无法看到你写了什么。 Andy Hunt建议用LEGO或积木表示系统中的对象。如果能提供一些看得到、摸得着的实物,就能帮助更多低级成员理解系统以及不同对象之间的关系。有时由于你的工作缺乏实体性,所以很难可视化表示和理解系统组件。不论是在白板上画出对象,还是在桌子上移动积木,都可以为系统建立一个 可视或可触摸的表示。
  • 记录接口并发布。可以使用一个打印文档、Web页面或者Wiki,但是不论采用哪一种媒介,都必须保证信息公开。对 象的接口绝对不能作为秘密。
  • 保证会议不被中断。要尽量减少转移话题和回答问题的次数。

这种集体参与的架构设计有很多好处,如下所列。

  • 定义产品的过程会成为团队成员的一个学习过程,特别是低级团队成员。
  • 团队共同创建架构时,会得到对整个系统的全面了解。
  • 团队会对他们设计的系统有很强的主人翁意识。如果只是交给你一个规范(由一个与世隔绝的僧人般的架构师所创建),你绝对不会有同样的感觉。

这里的目标是避免权利被剥夺,很多开发人员在被要求编写代码而不是考虑问题时都有这种被剥夺权利的感觉。如果把你的架构强加给你的团队,而且有些开发人员的正当权利被忽略,开发人员就会感觉自己像是一个机器中的齿轮。尽管一线编码人员非常了解技术,但是没有办法改善整个系统,这种感觉可能让人极其郁闷。让每个人都加入进来,就可以构建一个更好的系统,同时还能培训你的团队成员。

技巧20:集体参与建立架构

我们并不是说大楼里的每一个工作站都必须是生产环境的一个镜像。不过,至少应该有一个这样的工作站,你的自动构建和测试机就是一个理想的候选。

技巧21:如果生产环境会用到,你也要用到

要尽早解决最难的问题,把容易的留到以后解决。

技巧22:先解决最难得问题

技巧23:封装的架构是一个可伸缩的架构

技巧24:除非船在移动,否则转动方向盘没有用

曳光弹开发具有功能领域的封装、并行工作、快速的客户反馈以及很多其他特性,这使它成为较为灵活和有效的软件开发方法之一,你的下一个项目确实应当尝试这种方法。

大多数人在回避问题上花费的时间和精力比他们尝试解决问题所下的功夫还要多。 亨利·福特

技巧25:测试之前不要修改遗留代码

技巧26:使用测试驱动重构清理不可测的代码

模拟客户测试能在最少的时间内测试最多的代码行。

技巧27:模拟客户测试可以事半功倍

技巧28:持续测试不断改变的代码

技巧29:必须适用于所有人

技巧30:经常集成,并持续构建和测试

技巧31:尽早而且经常发布真实演示系统

总结一下,你可以采取以下措施:

  • 使用每日例会修正另类开发人员的航向;
  • 保证另类开发人员只能完成任务清单上的任务;
  • 使用代码审查和自动代码变更通知来跟踪另类开发人员的工作;
  • 使用CI作为最后一道防线监视另类开发人员的工作。

技巧32:公布你在做什么以及为什么这么做

技巧33:会面才能建立真正的团队

如果没有需要修正的问题,就不要尝试引入一个新实践或新过程。绝对不要因为一个实践是“正确做法”就盲目引入。相反,要找出你的工作室真正的问题,然后明确如何修正。这样一来你会拥有一个平稳运转的工作室,因为你只修正有问题的地方。

技巧34:只修正需要修正的地方

应当选择一个合适的时间引入实践,尽量减少对关键活动的破坏。

技巧35:破坏性的“最佳实践”不是最佳实践

技巧36:自下而上改革

技巧37:要具体展示,不要光说不练

技巧38:让管理层逐渐认可

技巧39:测试有bug的代码

技巧40:任务清单是一个活动的文档,生活中处处有变化

技巧41:如果任务清单上没有,那就不是项目的一部分

及时的反馈会有惊人的促进作用。不要浪费这个机会。

技巧42:要快速做出反馈

附录A 技巧汇总

  1. 选择习惯
  2. 留在沙箱里
  3. 如果需要就将其签入
  4. 从第一天起就使用脚本构建
  5. 任何机器都可以作为构建机
  6. 持续构建
  7. 持续测试
  8. 避免集体失忆
  9. 演练产品——自动测试
  10. 使用通用、灵活的自动化测试框架
  11. 工欲善其事,必先利其器
  12. 使用开放格式集成工具
  13. 使用熟悉的关键路径技术
  14. 按照任务清单工作
  15. 要有一个技术领导人
  16. 通过每日例会频繁进行航向修正
  17. 可以说“以后再来”
  18. 经常审查所有代码
  19. 目标是软件,而不是遵从过程
  20. 集体参与建立架构
  21. 如果生产环境会用到,你也要用到
  22. 先解决最难的问题
  23. 封装的架构是一个可伸缩的架构
  24. 除非船在移动,否则转动方向盘没有用
  25. 测试之前不要修改遗留代码
  26. 使用测试驱动重构清理不可测试的代码
  27. 模拟客户测试可以事半功倍
  28. 持续测试不断改变的代码
  29. 必须适用于所有人
  30. 经常集成,并持续构建和测试
  31. 尽早而且经常发布真实演示系统
  32. 公布你在做什么以及为什么这么做
  33. 会面才能建立真正的团队
  34. 只修正需要修正的地方
  35. 破坏性的“最佳实践”不是最佳实践
  36. 自下而上改革
  37. 要具体展示,不要光说不练
  38. 让管理层逐渐认可
  39. 测试有bug的代码
  40. 任务清单是一个活动的文档,生活中处处有变化
  41. 如果任务清单上没有,那就不是项目的一部分
  42. 要快速做出反馈