本文综述了嵌入式系统敏捷开发(Agile Development),敏捷宣言,敏捷的原则,很多的敏捷开发的实践习惯,敏捷开发与瀑布开发流程的区别,敏捷的任务stories划分,并行开发、敏捷的时间安排,敏捷的通信交流方式等等。敏捷开发的其他别名包括极限编程(Extreme Programming),特性驱动的开发(Feature Driven Development (FDD)), 迭代式增量软件开发Scrum,完全清楚(Crystal Clear)以及DSDM动态系统开发方法(Dynamic Systems Development Method)。
为什么嵌入式系统软件开发要采用敏捷开发呢?并不因为敏捷开发是现在的流行词,更重要的是因为敏捷开发能提高你的效率,基于计划驱动的实现方式会掩盖很多的问题,当你发现时也许就晚了。计划的开发并不能提供足够的自信来掌控产品的发布时间。软件开发项目往往受制于较长的开发周期、推迟的发布、无法预测的时间安排、较差的质量、不合客户预期等。这些所有的问题会构成一个正的恶性循环,如下图1所示。
图1. 嵌入式开发的恶性循环 Click on image to enlarge.
模糊的需求
上面的恶性循环中有一些正反馈,如计划安排、修改缺陷、以及需求更改的恶性循环。这正是迭代实现的敏捷开发可以有效解决的问题。基于瀑布的软件开发流程定义需求-系统设计-测试-实现-测试-发布的实现听起来很诱人,但通过时间的考验证明往往不能满足需求。迭代开发,很多年以前就作为敏捷开发的重要的实践方式,从1987年Fred *s作为国防部军事软件开发上建议从瀑布流程转换成迭代开发开始就意味着瀑布模型在大型的DoD软件项目上的失败。在过去的十余年间,敏捷开发更多的是和软件开发联系在一起而不是和嵌入式,虽然嵌入式有其特殊性,但采用敏捷开发同样有增益,因为嵌入式开发也有同样的问题。
什么是敏捷开发?
敏捷宣言包括:
- 个体和交互 胜过 工具和过程
- 可以运行的软件 胜过 面面俱到的文档
- 客户合作 胜过 合同谈判
- 响应变化 胜过 遵循计划
个体和交互胜过工具和过程
敏捷强调个人交互和团队合作的重要性,很多开发流程总是把人的因素从软件开发中排除,但是敏捷宣言的首要表述就是强调人和交互,工具是需要的,但是在团队中的人创造了成功的软件产品。
可以运行的软件胜过面面俱到的文档
第二点是对开发流程的度量,文档也许有价值,但可以工作的软件更有意义。但敏捷也并不意味着不写文档。只是说文档需要时间来写和维护,很多时候文档只是因为我们的流程需要文档而已,而敏捷开发者则能度量写文档的意义,而创造客户需要的文档。另外需要记住的是文档并不能表明项目进度的,敏捷开发则是当50%的项目特性实现时才算项目50%完成了。虽然嵌入式软件只和硬件一起交付一次,但并不表明不能用特性来跟踪进度.
客户合作胜过合同谈判
这点是强调和客户的紧密合作。客户的交互很重要是因为软件很难在初始时就能确定具体的特性和features。需求和市场往往瞬息万变,客户早些介入很更好的表明他们的需求。
响应变化胜过遵循计划
最后一条是对现实中的复杂情况的应对。计划很重要,但是情况变化很快也需要经常的变换,但这并不意味着敏捷开发没有发布的日期,而日期和承诺在敏捷开发中看的很重。敏捷开发者会在短的时间周期内来度量和计划的一致性。
敏捷开发的基本原则:
1. 优先级最高的是,通过早期和持续交付有价值的软件来满足客户。
2. 欢迎变更需求,即使在开发的后期提出。敏捷过程为客户的竞争优势而控制变更。
3. 以两周到两月为周期,频繁地交付可运行的软件,首推较短的时间定量。
4. 在整个项目过程中,每一天开发人员都要和业务人员合作。
5. 由个体推动项目的建设,为个体提供所需的环境,支持和信任。
6. 在开发团队中或开发团队间传递信息的最为有效和高效的方法是面对面的交谈。
7. 衡量进展的重要尺度是可运行的软件。
8. 敏捷过程提倡可持续的开发。
9. 发起人,开发者和用户应该步调一致。
10.不断地关注技术上优越的设计会提高敏捷性。
11.简洁是最重要的,简洁就是尽量减少工作量的艺术。
12.最佳的架构,需求和设计来自于自组织的团队。
13.团队要定期反省如何使工作更有效,然后相应地调整行为。
迭代开发
敏捷开发是一种迭代和增量开发(iterative and incremental development (IID)),IID通过把项目分割成一各个的2周到4周的迭代周期来获取反馈信息,每次迭代输出的都是可以工作的软件开发结果。每次迭代相当于一个单独的项目,能发布一个可以应用的产品。当然在初始的迭代中,也许只能看到软件在测试环境中的情况或者原型。敏捷开发中的成员角色分为客户和开发者,客户定义和测试产品,开发者创建产品。嵌入式软件工程师需要理解反馈,设计的控制系统通常都有反馈机制来让系统可控。基于迭代的敏捷项目能提供对项目至关重要的参数,如进度安排、需求和设计。把敏捷当做软件开发的控制系统好了。项目预测、计划和组织工程为若干个迭代。发布的节奏有利于建立一个可以测量的速度,从而校正开发计划,监控进度。如每次迭代完成20个点,而挤压的工作有200个点,那么可以说10次迭代完成工作。如果要求在8次迭代完成,那么就需要增加人手,消减功能或者加班了。
需求被分割成可以描述的部分,可以估计、测试和发布的单元。这些单元小到可以在一个迭代内完成,当开发者完成就能及时从客户那里得到反馈。在嵌入式开发中,初始的几次迭代因为硬件还没有完成,也许无法测试和仿真软件的功能,但可以展示实际工作的demo。通过增量的开发,开发者会随着进展得到不断的反馈。增量和演进的设计实现是敏捷开发的关键,软件需求不断演进,任务优先级也不断变化,软件期望随着市场和硬件的变动还是可以使用的,因而代码写出来就是需要改变的,在产品的周期内设计和编程需要不断的变化。增量开发还有利于对发布日期和实现功能的管理,开发团队的进度记录很容易基于事实来预测这些而不是凭想象。过去的瀑布开发下,相当多的时间被花在前期根本不会再最终产品中露面的事项。敏捷开发中,时间被首先安排在最高优先级的特性和能力上,需求和开发过程融合,因而很少浪费时间。对于项目的审视在细节级别体现,如果你有10个必须包含的特性,他们被分成若干个更小的部分,产品内容也按照细节级别管理,因而可以把不太重要的特性推迟发布。
并行工程
想象如下的并行轨道的火车,分别代码硬件和软件开发工程。
需求的火车比开发的火车出发的早,即首先发现需求,定义需求并转交给开发部分。分阶段的开发只有一个轨道,在需求没有完全定义结束前,不能开始开发。 虽然有可能在需求都清楚之后开发过程会更快,但一直等待需求确定再开始开发实在是需要花费相当大的时间。
并行开发是缩短软件产品进入市场时间的策略,让串行的过程变得并行,并行开发是敏捷的重要思想,增量开发不用清楚的知道最终的目标如何。并行开发使得更早的进行开发周期,因为需求并不是一直都清楚的,开发的需求总时刻在变。很多的项目团队就因为不清楚需求而瘫痪,总是期望能先描绘出清晰的图像然后开始完美的体系结构设计。这听起来很美好,但是产品开发非常复杂,因而在开发初期,只要了解了产品的几个核心特性,然后根据项目进展和市场反馈来不断的完善需求,调整设计中原先预计的不足。初期的实现也开拓了需求和设计,增加了团队的知识,能更明确需求和软件设计。初始的对高价值高风险的特性的开发也给客户和开发者一些反馈信息来帮助防范创造错误产品或者架构的风险。不确定性和风险并不仅限于软件,对硬件同样适用。
嵌入式开发者不用等待硬件的完成来开始软件的开发,软件和硬件开发同样需要并行。软件开发中先抽象硬件,确定好接口和硬件的交互方式,并不需要在弄清楚具体的实现细节。硬件的抽象让软硬件设计和测试在硬件可用前就可以进行,还能给硬件设计提供相当的需求信息。这个从需求收集、风险防范、开发实践的迭代开发意味着能更实用客户需求的变化,项目目标和硬件架构的变化。因而需求、软件和硬件并行开发更能保证进入市场的时间。这种并行的模式让大部分的开发在需求完全确定前完成。
图2. 瀑布式到并行开发 Click on image to enlarge.
自动测试
软件设计的复杂性和循序渐进意味着可以把软件开发分成若干可以工作的软件。一个小小的bug可能需要耗费数月的时间来解决,因而早期就考虑发现软件中的bug非常重要,现在的很多产品都通过在实际硬件上手工测试来发现问题,手工测试有很多问题,不但耗时而且可能还需要专门的测试环境,因而这些测试很难时刻运行。我们需要的是可以一键就能告诉软件设计是否符合说明文档。敏捷团队会自动进行单元测试和验收测试,一旦代码有变化就能进行测试,自动测试验证新引入的特性没有破坏已存在的特性。测试相当于复式会计,借贷要平衡的。自动单元测试是软件开发者根据测试驱动开发(Test-Driven Development,TDD)得到的反馈来完成,单元测试用于表明代码和预想中是否相同,一个给定的模块是否工作正常;验收测试是测试系统是否满足需求,所有模块的合并是否工作正常,开发者和测试工程师根据客户需求来写系统测试,验收测试可能在开始迭代开发前就开始写了,这也让测试工程师更深入的加入项目,而不是在瀑布模型的最后端。当然在嵌入式平台进行这些测试也许需要测试环境,即硬件平台,但是可以采用虚拟实现来抽象硬件来进行单元和验收测试。
硬件抽象是定义了硬件服务的接口,图3是一个安全系统图。SecuritySystem包括两个部分:Arm和HandleSensorActive,Arm键用来传感硬件驱动和事件回应,HandleSensorActive是特性传感器发生变化时才调用的.SecuritySystemImplementation告诉HardwareControl接口来TurnOnAlarm,当运行自动测试时,输入事件并不来自实际硬件而是来自测试系统,Hardware Fake-out层来产生事件。 需要注意的是,这种虚拟设备的模拟并不能反映实际硬件平台的性能,需要单独的performance test。
图3, 独立测试单元来测试应用功能 Click on image to enlarge.
迭代的工作流程
非嵌入式软件敏捷开发每个迭代周期团队会发布软件,但嵌入式增量开发小的增量就重新发布并不实际,尤其在还有并行的硬件开发的情况下,即便如此嵌入式系统敏捷开发的迭代周期还是很有意义的。没有硬件环境的情况下,可以用demo仿真和评估。迭代式固定时限的时间窗口,即便有些事物没有完成,这个时间段也不会延长,及时工作完全完成也不提早结束。一般周期不长,2个星期左右的计划就行以避免不确定性对长期计划的影响。迭代周期不长意味着要做很好的任务划分成小的功能单元,这个功能单元叫story。每次迭代需要完成所有的stories并自动化这些story的验收测试。
Stories
敏捷开发的一个很大的挑战就是把系统划分成增量和可预见的stories。嵌入式系统因为复杂的软件和硬件迭代而更不容易划分stories。产品的story表达的是价值,进度或者减少风险。Story描述为整个系统的举止的详细一部分。在这种意义上,story可以认为是使用实例。使用案例里有乐观路径和变数,乐观路径是当一切顺利的结果,而变数代表的是系统处理的特殊案例,如处理错误信息。嵌入式开发中还有控制和硬件通信的部分,这些硬件相关的stories从零开始的划分成在两个星期内完成的单元很不容易。
考虑一个安防系统的USB端口,顶层需求"USB port",划分成如下的stories:
- 打印事件log;
- 备份USB存储卡的配置;
- 恢复USB存储卡的配置;
通常的估计也许要3个月才能完成这个需求,这种长期计划的不可预见和不准确性会导致管理和开发的相互不信任,应该极力避免,因而应该把长期任务分而治之。以下是完成USB特性的进度展示:
- 和USB寄存器通信;
- 设备编程,确认时钟频率范围;
- 检测一个设备被插入到USB端口;
- 检测打印机;
- 弹出页面;
- 打印文本;
- 打印事件日志log;
- 检测存储卡设备;
- 打开和关闭存储卡内的一个文件;
- 从存储卡读文件;
- 写文件;
这些细分的stories的发布结果都可见。和并行的硬件开发一起,备份和恢复的功能也可以独立开发。很多情况下,通过适当的假设,就能把任务划分成独立的stories。
测试驱动开发(TDD,Test-Driven Development)
测试驱动开发是一个通过在开发代码的同时并行的写自动化测试代码的开发实践。包含如下步骤:
- 创建测试;
- Build, Run所有的测试,看其中失败的案例;
- 写代码让测试通过;
- Build, Run所有的测试
- 重构已去除副本;
首先构建测试,然后写实现来通过测试,这种基于定义需求,实现的反馈循环,增量开发,反馈结束到一个新的里程碑。测试驱动开发可以预测的工作流程很快取代了不易预测的基于测试、debug的开发流程。
多目标开发—平台无关性
TDD的开发流程意味着嵌入式软件的平台无关性,面向对象的设计原则用来创建松散耦合的软件模块,嵌入式软件设计来和硬件、操作系统以及其他子系统接口交互的,子系统和模块必须保持独立性才能在集成到最终硬件前被测试。项目初期,开发系统或者评估系统是唯一的测试设备,在EVM评估板或者开发系统中测试可以让测试很早就开始,进展的发现应用中的问题。
Design设计
敏捷设计时循序渐进的,开发前无法试图得到完整的高层布局和设计图。但这并不是说敏捷不需要预先设计,对于大的软件开发团队而言预先的设计时必要的。敏捷项目开发初期是迭代0(Iteration Zero)或者探索期,初始的目标以及stories,确定初始的软硬件划分以及初始的软件架构。软件架构表明系统的框图和主要的子系统。设计图表明范例命令、查询以及允许子系统协作的事件。软件架构视图不是一个全面的设计文档,也许只是在白板上做些记录,但对于大团队而言,还是要布局好一个大家共同努力的视图的。软件架构图不是一个子系统一个子系统实现后的集成,stories的实现方式把设计划分,以精简的模块和子系统开始慢慢丰富内容。
敏捷开发并不规定开发团队需要的文档,团队可以通过组内的有效通信,多个团队或者分布式的项目需要更好的文档和交流。有些特定商业领域如药材设备需要文档需求以及功能需求。你可以根据特定的需求来采用敏捷方式,并根据自己所学而不断进化。当文档对团队有价值时或者被客户需要时,团队应该找到有效的构建文档方式,如需要设计架构文档时,团队应该在架构的一部分正确工作并测试后来产生正式文档,当然首先是一个粗略的设计草图,然后根据确认结果来产生正式文档。这就避免了因为修改设计而反复修改文档的effort。
Release发布计划
客户和开发者一起来指定有一系列的迭代开发和明确的日期规定的发布计划,每个迭代发布包括一系列的stories。在发布计划制定时,这些stories被评估和安排,通常简单的story被安排为1分,其他相对困难的给予更高的分。一次release给予尽可能相同的速度,即评估的分数总和。
经过一段时间的迭代开发,团队会知道自己的速度,即所有stories的分数总和。这个速度可以用来反映团队的进度。图5是一个速度追踪表
图5. 敏捷开发速度追踪表 Click on image to enlarge.
如果估计的速度太乐观了,乐观程度需要团队跟踪以确定实际可行的速度。图6是一个检测项目进度的燃烧表(burn-down chart)。
图6,检测项目进度的燃烧表Click on image to enlarge.
速度跟踪和燃烧表检测可以给时间安排提早给出警示,及早做出正确的举动,如减少范围,增加人手,延长时间等。最实际的方式是调整项目的范围,即增减项目的功能,因为系统特性被划分为stories,因而方便增删和重新安排。敏捷计划基于长期计划的不准确,下一个release plan的细节和增量开发比较精确。随着时间的推移,计划就变得更为合理和准确。
组织架构影响
敏捷开发包含很多的技术点,敏捷的迭代特性会改变整个开发架构,如果把敏捷作为解决技术难点的话,那采用敏捷也许没什么效果,成功的采用敏捷需要管理团队和开发团队一起对解决导致项目延期、不准确的估计和低质量的问题感兴趣。技术规范、日程安排和计划不能直接扔给开发者,管理人源和开发者应该共同引导项目在需求、资源和日期等约束下的一次次的成功发布。为引导开发团队,需要一个客户团队,产品经理在测试工程师、产品定义和系统工程师帮助下领导客户团队,当有软硬件交互时,还需要硬件工程师的代表。
Communications敏捷交流
敏捷团队欢迎交流,通常下面的方法来保证大家在同一阶段:
- 迭代0(Iteration Zero) –一个新发布的开始
- 定期的发布计划会
- 迭代检查回顾—每两周一次,接下来就是迭代开始会
- 迭代开始会
- 每天的站着的scrum会
每天的会议内容包括,你昨天干什么了,你今天打算做什么,你打算怎么做。会后可以进行更深入的讨论。
迭代检查回顾时,之前的迭代周期stories和验收测试,记录速度。研究什么进展很好,碰到过什么问题,哪些可以改进
总结
本文综述了嵌入式系统敏捷开发(Agile Development),敏捷宣言,敏捷的原则,很多的敏捷开发的实践习惯,敏捷开发与瀑布开发流程的区别,敏捷的任务stories划分,并行开发、敏捷的时间安排,敏捷的通信交流方式等等。敏捷开发的其他别名包括极限编程(Extreme Programming),特性驱动的开发(Feature Driven Development (FDD)), 迭代式增量软件开发Scrum,完全清楚(Crystal Clear)以及DSDM动态系统开发方法(Dynamic Systems Development Method)。
References:
敏捷开发实践:
- Beck, Kent, Extreme Programming Explained
- Cohn, Mike, Agile Estimation and Planning
- Cohn, Mike User Stories Applied
- Martin, Robert, The Principles, Practices and Patterns of Agile Software Development
- Martin, Robert, Clean Code
- Book: Test-Driven Development for Embedded C
- Papers: http://renaissancesoftware.net/papers.html
- Blog articles: http://renaissancesoftware.net/blog
"Agile Embedded Software Development" James Grenning on Tuesday, May 3rd, 2011 in San Jose, CA
http://zh.wikipedia.org/wiki/%E8%BD%AF%E4%BB%B6%E5%B7%A5%E7%A8%8B
http://en.wikipedia.org/wiki/Agile_software_development
http://houh-1984.blog.163.com/
Larman, Craig and Basili, Victor, Iterative and Incremental Development, a Brief History, IEEE Software, June 2003 Cover article
http://agilemanifesto.org/principles.html
Grenning, James. Test-Driven Development for Embedded C, Pragmatic Bookshelf, 2011 (www.pragprog.com/titles/jgade)
Grenning, James, Test-Driven Development for Embedded C, Why Debug?, ESC-324, San Jose 2011
Grenning, James, Testing Embedded Software with Executable Use Cases, ESC-244, Boston 2011
Grenning, James, Object Oriented Design for Embedded Software Engineers, ESC-209, San Jose
Beck, Kent, Test-Driven Development, Addison Wesley, 2002
http://renaissancesoftware.net/papers.html
本文综述了嵌入式系统敏捷开发(Agile Development),敏捷宣言,敏捷的原则,很多的敏捷开发的实践习惯,敏捷开发与瀑布开发流程的区别,敏捷的任务stories划分,并行开发、敏捷的时间安排,敏捷的通信交流方式等等。敏捷开发的其他别名包括极限编程(Extreme Programming),特性驱动的开发(Feature Driven Development (FDD)), 迭代式增量软件开发Scrum,完全清楚(Crystal Clear)以及DSDM动态系统开发方法(Dynamic Systems Development Method)。