
归根到底,软件实现质量决定项目成败。决定实现质量的因素有两个:
众所周知,无论开发者如何小心,随着开发的不断进行,系统中缺陷(或称为Bug)的数量都在不断积累。在传统软件开发方法中,测试往往被推迟到实现之后进行,因而不断积累的Bug不能被及时的发现和修复。这将带来如下后果:
为了保证实现质量,采用传统开发方法的团队也做了很多努力,例如明文规定单元测试覆盖率、定时进行代码复审等。但效果未必理想,一种情况是:由于实现和测试在实现时间上分离,测试运行不及时、不频繁,在进度压力下,这些制度往往不能真正落实;另一种情况是:很多测试只是追求覆盖率,没有真正起到充分测试的作用,甚至出现假测试。代码复审会议也流于形式,对于改进代码质量的帮助不大。于是我们看到一个现象:一边是公司不断强调质量管理,另一边软件还是Bug重重,代码修改难度很大。由此可见,要保证软件质量,只有制度还是不够的。
大型企业级应用系统往往是由多个模块或者子系统组成的,因此除了基本的开发活动,与系统质量、成败密切相关的另一个问题是集成。
传统开发方法通常在开发结束之后才进行系统集成和验收测试,这就导致这个阶段成了一个问题高发阶段。系统经常在集成阶段集成不起来,大量以前没有预料到的BUG突然出现,团队需要花费很多时间来解决这些问题,给项目造成了很大的风险。有些项目,在开发实现阶段感觉已经接近结束了,但一到集成阶段就问题重重,使得“上线”成了一件令人恐惧的辛苦工作,需要在客户现场加班加点才能成功上线。再加上验收测试突然提出大量修改意见,愈发增加了这一阶段的工作量和难度。
随着人们对软件开发认识的加深,可测试性(testability)逐渐被提升到了开发活动中第一要素的位置。而传统软件开发方法陷入困境的原因恰恰在于对可测试性的忽视。除了基本的验证功能的正确性,可测试还具有更深层次的含义:
在传统开发方法中,代替自动化测试的往往是大量的手工测试,这样测试除了成本高、耗时长外,还存在难以保证测试一致性和质量,从而无法担当起保证软件质量的重任,更对架构设计毫无贡献。
敏捷实现的核心实践包括:
测试驱动开发并不是简单的“先写测试,还是后写测试问题”,该实践的要点在于“驱动”二字,体现在三个方面:
应用测试驱动开发的最佳实践是结对编程,在结对过程中,通过结对双发的交流、讨论、轮流编写测试和实现,可以实现:
持续重构是测试驱动开发的关键一环,也是系统演进的手段,而简单设计则是系统演进的目标。
简单设计不代表简陋的设计,简单的设计往往是历经重构、精炼才能得到的。简单的设计易于理解、易于添加新的功能、易于维护,随着需求的变更,以及开发者对领域的认识加深,当前的简单设计有可能不再使用,进而又需要通过重构实现需求变更后的简单设计。可以看出:测试驱动开发,重构,简单设计是环环相扣,互相支持的,而进行这些实践的最佳方式就是结对编程。
在敏捷项目中,系统集成不再是某个特定阶段才有的活动。集成自始至终贯穿整个开发活动,从“某个离散时间点上的活动”转变为“开发过程中不断进行的连续活动”。例如,在我们的敏捷项目中,一般一对开发者一天内会继承5~10次之多,则一个由5~6对开发者组成的团队一天可能会集成25~50次之多。这样,集成的成本和风险都被平摊到了最低限度,而由集成引发的Bug也能在第一时间得到修复。