瀑布模型
2019年11月13日
在软件世界中,“瀑布”通常用来描述一种软件开发过程,它与迭代或敏捷风格形成对比。就像软件中许多众所周知的术语一样,它的含义定义不清,起源也模糊不清——但我发现它的本质主题是将一项大型工作分解成基于活动的阶段。
不清楚“瀑布”这个词是如何流行起来的,但大多数人将其起源归因于温斯顿·罗伊斯的一篇论文,特别是这张图
虽然这篇论文似乎被普遍认为是瀑布概念的来源(基于向下级联任务的形状),但论文中从未出现过“瀑布”这个词。不清楚这个名字后来是如何出现的。
罗伊斯的论文描述了他对当时(60年代后期)软件开发过程的观察,以及如何改进通常的实施步骤。[1] 但“瀑布”已经走得更远,被用来作为一种软件开发风格的通用描述。对于像我这样在软件会议上发言的人来说,它几乎总是以贬义的方式出现——我记不起有哪位会议发言者在多年来对瀑布说过什么好话。然而,在与企业实践者交谈时,我确实听到过他们将其视为一种可行的,甚至更受欢迎的开发风格。当然,现在比90年代少得多,但比听过程专家所说的要频繁得多。
但“瀑布”到底是什么?这不是一个容易回答的问题,因为就像软件中的许多事情一样,没有明确的定义。在我看来,有一个共同的特征主导了人们对瀑布的任何定义,那就是将工作分解成基于活动的阶段。
让我解释一下这句话。假设我要构建一些软件,我认为构建它大约需要一年时间。很少有人会高兴地说“离开一年,完成的时候告诉我”。相反,大多数人会希望将这一年分解成更小的块,这样他们就可以监控进度,并相信一切都在正轨上。那么问题是如何进行这种分解呢?
正如罗伊斯草图所示,瀑布风格是通过我们正在进行的活动来实现的。因此,我们的为期一年的项目可能会被分解成两个月的分析,然后是四个月的设计,三个月的编码,以及三个月的测试。这里的对比是迭代风格,我们会采用一些高级需求(构建一个图书馆管理系统),并将它们分解成子集(搜索目录,预订书籍,借阅和归还,评估罚款)。然后,我们会选择其中一个子集,花几个月时间构建工作软件来实现该功能,将其交付到暂存环境或最好是交付到实时生产环境中。完成一个子集后,我们会继续进行其他子集。
在这种思维方式中,瀑布意味着“一次对所有功能执行一项活动”,而迭代意味着“一次对一项功能执行所有活动”。
如果“瀑布”这个词的起源很模糊,那么这种基于阶段的分解是如何起源的也是如此。我的猜测是,将一项大型任务分解成不同的活动是自然的,特别是如果你将建筑施工作为灵感来源的话。每项活动都需要不同的技能,因此让所有分析师在引入所有编码人员之前完成分析是有道理的。在人们开始编码之前,解决对需求的误解似乎更便宜——尤其是考虑到60年代后期计算机的状态。最后,相同的基于活动的分解可以作为许多项目的标准,而基于功能的分解则更难教授。[2]
虽然不难找到人们解释为什么这种瀑布思维对于软件开发来说不是一个好主意,但我应该在这里总结一下我对瀑布风格的主要反对意见。瀑布风格通常将测试和集成作为循环中的最后两个阶段,但它们是开发项目中最难预测的元素。这些阶段出现的问题会导致之前阶段的许多步骤需要返工,并导致项目严重延误。很容易宣布除最后阶段以外的所有阶段都已“完成”,而遗漏了许多工作,因此很难判断项目是否进展顺利。在所有功能完成之前,没有机会进行早期发布。所有这些都给开发工作带来了很大的风险。
此外,瀑布方法迫使我们采用预测式的计划方式,它假设一旦你完成了某个阶段,比如需求分析,那么产生的交付物将成为后续阶段工作的稳定平台。[3] 在实践中,绝大多数软件项目发现他们需要在几个月内大幅度更改需求,因为每个人都对领域、软件环境的特性以及业务环境的变化有了更多了解。事实上,我们发现交付一部分功能比任何事情都更有助于澄清接下来需要做什么,因此迭代方法使我们能够转向适应性计划方法,在该方法中,我们根据对实际软件需求的了解更新计划。[4]
这些是我轻率地说“你应该只在想要成功的项目中使用迭代开发”的主要原因。
瀑布和迭代可以相互嵌套。一个为期六年的项目可能由两个为期三年的项目组成,这两个项目都以瀑布风格构建,但第二个项目添加了额外的功能。你可以将其视为顶层有两个迭代的项目,每个迭代都是一个瀑布。由于规模庞大且迭代次数少,我认为这主要是一个瀑布项目。相反,你可能会看到一个项目有16个迭代,每个迭代持续一个月,每个迭代都以瀑布风格进行计划。我认为这主要是一个迭代项目。虽然理论上存在一个难以分类的中立项目,但在实践中,通常很容易判断哪种风格占主导地位。
瀑布和迭代可以混合使用,早期阶段(需求分析、高级设计)以瀑布风格完成,而后期阶段(详细设计、代码、测试)则以迭代方式完成。这降低了后期测试和集成阶段固有的风险,但不能实现适应性计划。
瀑布通常被视为敏捷软件开发的替代方案,但我认为这并不完全正确。当然,敏捷过程需要迭代方法,不能以瀑布风格工作。但很容易遵循迭代方法(即非瀑布方法),但不是敏捷的。[5] 我可能会这样做:将100个功能分成十个迭代,在接下来的一年内完成,然后期望每个迭代都能按时完成其计划的功能集。如果我这样做,我的初始计划是一个预测性计划,如果一切顺利,我应该期望工作能够紧密地遵循计划。但适应性计划是敏捷思维的一个重要组成部分。我期望功能在迭代之间移动,出现新的功能,许多功能被丢弃,因为它们不再具有足够的价值。
我的经验法则是,任何说“我们之所以成功是因为我们按时按预算完成”的人都在考虑预测性计划,即使他们遵循迭代过程,因此他们并没有以敏捷的心态思考。在敏捷世界中,成功完全取决于商业价值——无论几个月前计划中写了什么。计划是制定的,但会定期更新。它们指导我们决定接下来要做什么,但不会被用作成功衡量标准。
进一步阅读
Laurent Bossavit 在他出色的著作《软件工程中的小精灵》中,对“瀑布”一词兴起的起源进行了调查。
注释
1: 已经有很多试图解释罗伊斯论文的人。有些人认为他的论文反对瀑布,指出论文讨论了图2中我引用的那种过程的缺陷。当然,他确实讨论了缺陷,但他还说,图示方法是“从根本上来说是合理的”。当然,这种基于活动的项目分解在随后的几十年中成为了公认的模型。
2: 这导致了与“瀑布”一词相关的另一个共同特征——严格的过程,详细地告诉每个人他们应该做什么。当然,90年代的软件过程专家热衷于提出规范性方法,但这种规范性思维也影响了许多倡导迭代技术的人。事实上,虽然敏捷方法明确反对这种泰勒主义思维,但我经常听到伪敏捷倡议遵循这种路线。
3: 一个阶段应该在下一个阶段开始之前完成,这是一个方便的虚构。即使是最热心的瀑布支持者也会同意,在实践中,对先前阶段进行一些返工是必要的,尽管我认为大多数人会说,如果执行完美,每个活动都不需要返工。罗伊斯的论文明确地讨论了相邻步骤(例如,在他图中的分析和程序设计)之间预期进行迭代。然而,罗伊斯认为,更长的回溯(例如,程序设计和测试之间)是一个严重的问题。
4: 这确实提出了一个问题,即是否存在瀑布风格实际上比迭代风格更好的情况。理论上,瀑布可能在对需求和所用技术有深入了解的情况下效果更好——而且这些情况在产品生命周期中都不会发生重大变化。我说“理论上”,因为我还没有遇到过这种情况,所以我无法判断瀑布在实践中是否合适。即使那样,我仍然不愿意在后期阶段(代码-测试-集成)遵循瀑布风格,因为我发现将测试与编码交织在一起,同时进行持续集成,具有很大的价值。
5: 在90年代,面向对象的世界普遍认为瀑布是一个坏主意,应该用迭代风格来代替。然而,我认为当时并没有像敏捷社区那样拥抱不断变化的需求。
致谢
感谢 Ben Noble、Clare Sudbury、David Johnston、Karl Brown、Kyle Hodgson、Pramod Sadalage、Prasanna Pendse、Rebecca Parsons、Sriram Narayan、Sriram Narayanan、Tiago Griffo、Unmesh Joshi 和 Vidhyalakshmi Narayanaswamy,他们在我们内部邮件列表中讨论了这篇文章的草稿。