在过去十年或更长的时间中,软件开发团队一直受益于敏捷开发方法。他们采用这些迭代和增量开发实践,通过协作式开发推动解决方案的发展。传统的、非敏捷的软件创建方法通常依赖于一个更严格管制的开发流。瀑布流程就是这方面的一个示例,其中需求、设计、开发和测试的每个活动都是连续执行的。
虽然瀑布式开发多年来一直是大型的复杂系统开发的标准,但它有几个明显的缺陷。首先,即使需求会随时间而变化是众所周知的,但开发人员仍会努力在设计前完成文档,在编写代码前完成设计,其中有大量工作被浪费。另一个缺陷是,将测试和集成一直延迟到项目结束时才执行,问题往往发现得太晚,如果要解决问题,则有可能导致错过最后期限。这两个因素结合起来,在以较慢速度发展的世界中,也许可以容忍。但是,随着创建创新系统的压力的增加,这种方法的满足组织需求的能力在下降。
虽然敏捷实践是由开发 IT 系统的团队推广的,但它们同样适用于产品开发,其中的产品包括硬件、电子和软件。嵌入式软件开发与 IT 应用程序开发的主要区别在于部署目标资源(如处理器性能和内存)的有限可用性。嵌入式软件往往要在这些约束条件中执行复杂的实时操作。想像一下计算机控制的系统,例如汽车里的安全气囊。您需要他们立即部署,但需要的是可靠的部署。敏捷方法最初专为不受管制的行业中的在同一地点工作的较小型项目团队而设计。我们花了很多年对它们进行扩展,使敏捷方法可以容纳更大、更复杂的开发项目。
当应用为基于架构的方法的一部分时,持续集成(CI)和测试驱动的开发(TDD)扩展了基本敏捷实践,使之足以同时提供高品质和项目灵活性。本文将探讨在嵌入式软件开发的上下文中如何采用敏捷方法、CI、TDD。本文还说明了这个组合的好处。
如何将持续集成和测试驱动的开发融入敏捷实践中
现在,大多数人可能都已经听说过敏捷方法。它们带给软件开发的概念改变了团队组织的工作方式、适应不断变化的需求的方式以及发布软件的方式。持续集成(CI)是为敏捷开发而创建的,所以敏捷方法是任何 CI 讨论的背景。它将开发组织为功能性用户故事(user story)。这些故事按优先级分成较小的工作组,也称为冲刺(sprint)。
这里的思路是不要提前尝试解决每一个问题,相反,专注于您已经知道的东西。因此,团队设计、构建和测试他们对预期功能所知道的内容。这将在完整产品需求的一个子集的基础上创建一个工作产品。然后,团队继续到下一个最高优先级的需求集,并重复上述过程。当然,这是一个高度简化的视图,这个过程中有许多变化,但核心是:以增量方式构建您的产品,并尝试在过程中做一些改进。
根据 ThoughtWorks 的 Martin Fowler 的观点,持续集成(continuous integration)是一种软件开发实践,要求团队成员经常集成他们的工作。每个人至少每天集成一次,这导致每天有多个集成。集成是通过自动化的构建进行验证的,这些构建运行回归测试,以尽快检测集成错误。团队发现,这种方法会导致集成问题大幅减少,更快地实现有凝聚力的软件开发。
这导致 CI 流程的成功执行的最终细节。如果持续集成的思路是为了快速发现问题,从而向每个开发人员提供工作反馈,则必须以某种方式快速评估其工作。测试驱动的开发填补了这项空白。利用 TDD,您可以构建测试,然后等代码通过测试后再开发功能。随着每一个新代码的添加,可以将它的测试添加到在构建集成工作时运行的测试套件中。这将确保新增内容不会破坏之前出现的运作中的工作,并且事实上 “破坏了构建” 的代码的开发人员可以迅速获得通知。在图 1 中显示了持续集成和测试驱动的开发的典型组合。
图 1. 使用持续集成和测试驱动开发的敏捷实践
受益于持续集成的项目类型
少于 50 人,处理不太复杂的项目的团队绝对是敏捷开发和 CI 的试验场。但因为产品已变得 “更智能”,其复杂性也会明显增加。
进入传统产品的嵌入式软件的数量是惊人的。如今,一辆新汽车已较少以其马力作为卖点,而是以其嵌入式软件技术(例如,自动泊车、先进的安全警告、燃油效率、信息娱乐系统)作为卖点。为创建一辆新汽车而编写的代码行数多于为 F16 战斗机所编写的代码行数。
产品复杂性的增加,同时加速了新产品的上市时间。嵌入式软件的普及,结合更严格的最后期限,这些将敏捷实践和 CI 带到了嵌入式开发人员的面前。
使用敏捷方法实现嵌入式系统开发
敏捷方法让软件和系统团队能够快速响应变化。敏捷方法减少了与传统的软件工程相关联的时间进度风险,在传统方法中,组件的集成被视为后期阶段的工作。后期阶段的集成会引起对设计规范的误解,在发现问题时,对于要解决该问题同时又要满足其最后期限的团队而言,已经为时已晚。
然而,系统团队要生成的不仅仅是软件组件,他们对敏捷方法的某些方面持怀疑态度。他们说,删除过多的早期规划,最后就会获得糟糕的软件和硬件集成。如果没有早期的、经常的检查点来根据架构蓝图验证进度,团队可能无法生成可在更广泛的系统中正常运作的组件。此外,对于正在寻求设计的可重用性和可扩展至较大型项目需求的复杂系统的开发人员来说,敏捷方法看起来可能存在局限性。
这些担心是可以理解的,因为建模和架构不是敏捷技术的特点。但系统开发的 CI 方法对纯敏捷方法提供了多项改进。CI 帮助系统开发团队变得敏捷,并且能够响应快速的业务变化,在同一时间确保正在开发的实际硬件和软件都在不断同步。CI 使得团队成员能够在自己的领域组中有效地工作,集中精力于他们最擅长的任务。在每天结束时,他们知道,他们对项目的贡献被集成,并且各组件可以一起工作。如果有哪个组件无法集成,该组件很快就会被发现。
让我们来考虑复杂系统开发和交付的一些必不可少的组成部分,并探讨 CI 如何有助于迎接挑战。
架构
当您正在构建复杂系统时,如果没有蓝图,就无法不断添加新的特性。如果没有蓝图,那么在利用额外的迭代时,就会遭遇更多返工的情况。不管您将它称为蓝图、模型还是架构,它都提供一个开始迭代过程的坚实基础。
架构在最多 50 名团队成员的较小型项目中很有帮助,但是,如果超出了这个规模,就必须做一些前期工作,以了解组件化、重用和可变性。此前期分析使您可以拆分团队,但仍发布协调的产品。在让硬件和软件开发人员一起工作也是这样,就像对包括嵌入式软件的复杂系统一样。
仿真
通过在仿真模型中捕获架构,团队可以看到系统如何响应不同的输入。这种形式的早期测试可以验证系统是否如预期般执行,从而满足要求。这也使得设计人员能够可视化设计的任何意想不到的后果。在检查代码文本时,很难看出这些意想不到的后果。当查看系统模型时,它们会变得明显得多,在查看工作中的系统模型时,它们甚至变得更加明显。
在这种方式中,建模和仿真让测试和集成可以在设计工作开始时就立刻开始执行,从而消除了在嵌入式硬件尚未可用时可能遇到的延迟。它可以节省对那些不可行的早期架构原型所进行没有必要的大量投资。即使您的确有可用的硬件,持续集成也要求不断地构建。
越早需要看到结果,构建环境就会变得越昂贵。由于 CI 的主要目的是尽可能快地提供结果,仿真使您能够在无需支付超高硬件成本的情况下进行测试。它还提供了一个更简单的方法来沟通组件的功能,这对于敏捷开发中常见的结对编程和 “代码审查” 非常有价值。
构建自动化
持续集成要求构建自动化,也就是说,提供让软件自动编译和链接到可执行文件的能力。速度非常重要,因为大型构建可能需要很长时间。如果没有快速、可靠的构建,就会缺乏解决集成问题所需的洞察力。在运行集成构建时,会识别由两位或多位开发人员所做的更改之间的冲突,以便解决该冲突。所以,如果发现问题,要解决前一个构建的冲突的开发人员可以通过硬件仿真来测试更改后的代码,不会耽误其他开发人员的工作。但是,要实现这种效率,则必须不断进行集成构建,在上一个构建完成时就立刻开始新的构建。这与其他流程所使用的每天一次或每周一次的构建非常不同。
当然,这种方法要求构建自动化,因为要将一天中反复多次开始构建的任务指定给一个人是不切实际的。此外,构建应快速执行,这往往要求构建是多线程的。多线程的构建可以使用软件的不同组件,利用在某个其他组件上运行的构建并行地执行这些组件,从而加快汇总的构建时间。它的确需要更多硬件和更复杂的脚本。脚本变得越复杂,构建管理工具就会变得越有价值。
工作管理
主要的敏捷概念是将工作拆分成为易于管理的小块的价值。这也是 CI 的基本前提:及早地、经常地改正错误。这样就可以避免它们稍后在项目中发展成为更大、更难解决的问题。
该技术提供的好处之一是能够提供在项目时间表中多个日期进行构建和测试的更小的功能性发布。通过验证来自团队的架构、需求和时间表估算,每个交付都降低了项目风险。在敏捷方法中,尚未完成的工作被称为积压(backlog)工作。如果您开始将工作分配给小交付增量(被称为冲刺),分配给某个冲刺的工作被称为冲刺积压(sprint backlog)。分配给未来冲刺的剩余工作被称为项目积压(project backlog)。目标是将在冲刺定义的时间内可以实现的尽可能多的工作项分组到冲刺中。
此流程高度依赖于指标的收集,使团队可以更准确地预测任务所需的时间量,并推算出可以放入某个冲刺交付的任务量。然而,与这些指标等价的是,数据收集即使对于小团队也非常繁琐。当您将这些小团队组合在一起来产生更加复杂的产品时,任务会相当庞大,且无法手工完成。
市场上有很多产品可以帮助组织完成其工作,跟踪工作的完成状况,并生成与工作完成了多少、完成的速度、完成的质量等有关联的指标。如果遵循了 CI 实践,则必须将所确定的集成错误迅速添加到工作积压中,并作为高优先级移动到列表的顶部。在这方面,在市场上最好的产品提供了新工作项和构建管理系统之间某种程度的集成,因此,在每个构建后识别的错误就可以迅速得到修复,并与现有工作项集成,优先升级,并路由到正确的团队。
质量管理
质量管理是确保所有产品要求均已经过测试的开发生命周期实践。该工作需要得到组织和理解,使正确的测试可以在需求变更时得到更新。质量管理有助于项目经理回答以下问题:
•如果我的产品必须在下周发布,哪些部分将会产生最大风险?
•我们是否可以在没有低层要求的情况下发布产品?
•这是否是一个高品质的发布?
在面对加快交付周期的市场压力时,快速回答这些问题可以帮助业务充满信心地将产品发布到市场。管理人员可以更好地了解要添加哪些资源、在何处调整产品特性,以及何时重新建立交付日期,从而获得最大优势。
利用测试驱动的开发,测试的概念对于开发工作更加重要。在 TDD 中,需要根据需求编写测试,然后开发代码,直到它通过测试。这将确保没有创建额外的功能,即开发团队称为 “镀金” 的东西。即使额外的功能或特性似乎是一个好主意,但在不要求驱动决策时,额外的工作可能会提高成本,并增加交付时间。最终产品可能并没有实际提高客户满意度。
自动测试
在创建多个构建后,团队需要重新测试之前的版本中的可行功能。这个重新测试的流程以前被称为 “好代码(good code)”,现在称之为回归测试(regression testing)。它确保没有因为刚刚完成的变更而将错误引入或重新引入之前测试过的代码。利用 CI,可以将自动的回归测试编写为脚本,在每个构建结束时运行。这使得开发人员能够得到有关在在新构建中发现的错误的即时反馈。这一步是为了通知开发人员,他们所生产的新代码在按要求(或没有按要求)工作。如果没有回归测试,开发人员只知道构建已完成。由于无论如何都必须创建测试,所以 TDD 并没有添加额外的工作。它只是将工作的顺序颠倒了,首先创建测试,然后编写代码。
即使没有任何自动化测试,传统的瀑布式开发项目也是有可能生存的。可以描述和构建项目,然后让一大堆人不断对其进行测试。但只要您开始定期发布,这一流程就会出现问题。手动测试一个在一天中被多次构建的系统是不可行的。
协作
IBM® Rational® 软件组织长期以来一直主张协作是成功的系统开发和交付的一个关键因素。但在软件和硬件团队都参与的 CI 中,协作不仅包括从一个团队到另一个团队的有效构件移交,还包括对要求、特性和最后期限之间的权衡的全面协调的理解。
良好的架构可以支持这种协作,部分原因是人们可以更好地了解他们正在构建的各个组件之间的依赖关系。利用项目组合管理,您可以了解特性、重用和资源分配。但在合作开发的硬件和软件项目中,管理要求并制定有关何时可以更改这些要求以及何时不得更改它们的明智决策也很重要。
这些项目通常涉及决策的多个层次中的多个利益相关者。良好的协作有助于满足更大比例的利益相关者。它可以确保创建了正确的产品,并且可以快速识别来自更广泛的目标的偏差。这将产生可以更好地满足客户需求的产品。
结束语
从技术角度来看,CI 可以帮助团队更高效地工作。这些团队可能是跨职能的,创建配合工作的硬件和软件。他们在地理上可能是分散的,因为不断的集成工作将会确保您没有偏离设计。人们可以在大型团队中工作,因为复杂系统的不同组件将以更可靠的方式一起工作。CI 解决了许多非传统的敏捷团队在没有 CI 时可能都经历过的早期陷阱。CI 与测试驱动的开发相结合使更多人可以利用敏捷,因为它可以让敏捷方法更高效地工作。
从业务的角度来看,CI 可以提供更好的业务成果,让团队可以拥有自己的蛋糕并吃掉它。也就是说,通过在问题的早期并且在问题还是小问题时发现它们,而不是等到这些问题变成大问题且更难解决时才发现它们,团队可以将产品更快地推向市场。他们还可以在产品开发过程中更好地响应所引入的需求。敏捷开发将为客户创建更好的产品,这才是敏捷性的真正承诺。
原文来自http://www.ibm.com/developerworks/cn/rational/continuous-integration-agile-development/