我在去年做的几个项目里就开始尝试使用迭代开发方法,直到了07年第三季度时我拜读了的Robert Martin的《敏捷软件开发:原则、模式与实践》,于是就可以标榜自己是在尝试敏捷开发了,俺也XP了。
我这么XP了几个项目下来,没觉得项目组真正提高了多少需求响应速度,但很奇怪的是迭代开发方法的确大大提高了项目速度,尤其是ALPHA阶段特别明显。前阵子我一直没想明白,我并没有对部门固有的开发流程改变多少,工程师也都是第一次做项目的新手,但为什么进度能比以前快很多了呢?现在终于是想通了,原来迭代开发还有一个鲜为人知的好处,居然没有哪本书上写到。哈,个人发现。
什么好处呢?
我们先说软件的项目计划方法是从木土、机械等传统项目管理方法中继承学习下来的。盖房子显然是传统的瀑布型,先绑钢筋,然后盖模板,最后浇水泥,保养几天等水泥硬度足够后就再往上盖一层。首先,这是串行开发,顺序绝对不能颠倒;其次,这是绝对不可能用迭代方法的,比如先绑50%的钢筋,随便盖个60%的模板,然后倒30%的水泥,回过头来看看钢筋不够再往里头加剩余的50%钢筋。所以传统的甘特图也就特别适合表示这种“不能迭代的串行任务”。
而软件呢,我觉得不同于传统项目管理的主要特点之一,就是可以迭代开发。比如一个嵌入式系统内,我可以先简单把驱动层写个读写等主要功能,次要功能都留空;然后应用程序先调用这几个主要功能,UI上简单画几个对话框和按钮,这样系统能初始化、能读写就是第一次迭代了,验证完从硬件到UI的主要功能没问题后,再做第二次迭代把细节功能做上去,比如原来运行参数是在代码里写死的,现在允许动态修改啦,原来的函数结果出错就程序退出了,现在提高健壮性会有UI提示然后退回到出错操作前的状态啦,等等,最后才往UI上添加漂亮的图片。这种可迭代性是软件开发独有的。
好了,关键点就在这里:假设在该项目里,硬件、驱动、应用、界面这是一个串行开发过程(当然不是所有项目都会在这几个问题上串行),硬件需要驱动来验证正确性,驱动写完接口需要应用的调用才能工作,而应用又通过界面被调用,最终由用户操作。我们把任务抽象出来,假设在某次软件开发中,ABCD四个任务可能是像下图那样串行的。所以完成ABCD四个任务一共需要11天。实际上任务BCD只用了A的一部分功能。(比如应用程序的验证调试只需要被人机界面调用,而与花哨的贴图无关)
这时候我们用迭代方法,如下图。A先用2天时间出一个简单的版本,实现了B和C需要用到的功能,然后提交该版本后,BC任务立即可以开始,所以到D任务完成只需要9天时间。而A在完成迭代1后,立刻开始迭代2的工作。假设迭代1的代码在最终产品里一点都不用,A仍然花了4天完成他的模块,但截止到ABCD任务全部完成只需要9天。
结论如下:
(1) 通过迭代开发可以缩短串行任务中相互等待的时间。
(2) 只要发现任务变成串行的,会相互等待,那么就应该考虑快速出迭代版本来“解开串行”。
(2) 在迭代开发中,某些工程师的工作量被增加了,但从全局来看开发时间缩短了。
这就是本文标题所说“巧用迭代开发解开串行任务”的意思了。我还没看到哪本书上明确讲了用迭代开发可以解开串行任务等待。
但在实际操作中,最容易遇到的阻力就是:
(1)如果迭代1和迭代2由同一个工程师完成,那么多数工程师不愿意额外增加自己的工作量。
以上图的A为例,开发完任务模块需要4天,而多迭代一次则多做两天,更令他恼火的是,这多花的2天里做的第一次迭代只有4天的生命周期,4天后就被替代掉了,那些代码再也用不到了。我在没有明确提出这套理论之前,第一次迭代总是由自己完成,纯给别人做垫脚了。但这样一来工程师A也不乐意:你都把功能实现了,还要我干嘛?你自己写完得了。所以第二种阻力就是:
(2)如果迭代1和迭代2由两个人分别完成,那么迭代2的工程师工作积极性会受到打击,感觉是给迭代1擦屁股的。
这就是我要写这篇文章的主要原因。我们需要要有明确的理论指导,好让团队成员都明白用迭代方式解开串行任务的意义。卡耐基《人性的弱点》中描述了这种情形:激发他人高尚的动机。我们在迭代开发中多花费的个人精力、多写一些甚至最终产品里用不到的代码,但是为他人的模块垫脚,减少串行任务中相互等待,从而提高整个项目速度,这是一件挺高尚的事。在实施迭代遇到阻力时,对团队成员晓之以理,根据这套理论阐明工作意义,还是能取到比较好的效果。
下面我描述个我自己的具体操作案例:
在手机电视项目中,驱动提供DEMODULATOR到应用层的数据传送,应用层先把数据读出来后经过DEMUXER进行解析,析出AUDIO和VIDEO后,分别送给H.264 DECODER和AAC DECODER解码,解码后播放器进行A/V同步,最后送到GDI RENDERER和PCM RENDERER输出。所以关键路线图是
HARDWARE -> DRIVER -> DEMUXER -> AUDIO -> AAC DECODER -> PCM RENDERER
-> VIDEO -> H.264 DECODER -> GDI RENDERER
项目规划时,我可以立刻看到的串行任务有
1、DRIVER希望被播放器调用,将数据解析、解码、并输出,以证明DRIVER层的数据收发正常。但DRIVER和DEMUXER、DECODER等在任务层次同级,而低于播放器一级。也就是说即使最快的情况,DRIVER写好了准备调试,而DEMUXER, DECODER,RENDERER等也写好了,但播放器还没组装好。这时候DRIVER的调试和DEBUG就进入对播放器的串行等待中。所以这里需要一次迭代。
迭代A:专门为DRIVER写一个仿播放器行为的测试程序,可以从DRIVER读取码流并存成本地文件,可以进行搜台、换台、设置频点等操作。该迭代和DRIVER平级,在DRIVER完成时也必须完成。这样DRIVER对播放器的串行等待就被解开了。这个地方概念有点模糊,可以理解为一次播放器的初级迭代,也可以理解为对DRIVER的测试代码。
2、播放器希望被UI调用,以验证播放、暂停、时间显示、媒体信息等基本功能。但UI开发比较慢,按照客户要求的UI很炫很复杂,工作设计中心做图和评审交付还需要一段时间。这时候播放器可能进入对UI的串行等待中。所以这里也需要一次迭代
迭代B:先提供一个不加载图片的UI,按钮进度条滚动条等都是windows默认的灰色样子。能够覆盖播放器的调用接口,能够实现基本的状态切换。这样播放器完成后测试组就可以进行测试了,不必等UI全部完工。当然这次迭代不像迭代A在最终产品里用不到,这些覆盖所有接口的调用、状态机都是可以继续用下去的。
3、播放器希望尽早调试音视频同步,但H.264 DECODER解码速度偏慢,优化还需要比较长的时间。因此音视频同步功能会进入对H264 DECODER的串行等待中。
迭代C:先采用一个速度快、但很不稳定的第三方H264 DECODER LIB,运行出错了我们也没办法去改,但是正常工作时解码速度满足要求,可以进行音视频同步的调试。这就解开了音视频同步对H264 DECODER效率的串行等待。一旦我们自己的DECODER效率满足要求,就把这个LIB库替换下来。
实际上无论是XP也好,CMM也好,没有哪套理论是真正的银弹,教条作风对于工作无益,所谓招无定式,只有在合适的场合使用合适的方法有效推进了项目进展,才能算成功。
迭代方法可以有效地解开串行任务、可以让客户更早地看到产品、可以提高对需求变化的响应速度、可以靠持续的频繁的里程碑来鼓舞士气,但它也绝不是十全十美的,它增加了模块开发工作量、增加了集成工作量、增加了测试的工作量和压力,所以关键看运用。总体来说,当串行任务相互等待、人员利用率因此达不到100%时,迭代开发就是值得的;但如果人员利用率已经满负荷了,或者任务编排巧妙,避开了串行等待,那么对于子模块的迭代方法就要慎重权衡利弊.