现代软件工程讲义 7 开发 开发阶段的日常管理
[移山之道 14 章]
14.6 开发阶段的日常管理
荔荔:我今天真失败!在办公室里坐了10个小时,但是真正能花在开发工作上的可能只有3个小时,然后我的工作进展大概只有两个小时!
阿超:那你的时间都花到哪里去了?
荔荔:就是我们以前说的 “我没看见你在写软件,你到底在忙什么” 上面列出来的破事儿。每一件随机事情看起来都是挺重要的,我就放下手里的开发工作。但是好不容易做完了,刚想进入状态,又一件随机事情来了……
阿超:你要硬着心肠,说 “不”。
当场景、功能都计划好的时候,要给员工足够多的时间,让他们投入到工作中去,而不要经常打断他们。
要尽量减少非开发时间,不要动不动就开“全体会议”。团队成员们自我时间管理也很重要。由于MSF鼓励沟通,TFS也设置了不少提示(Alert)自动报告全体成员项目各方面的情况,因此团队的E-mail会特别多。在这种情况下,不要整天被E-mail牵着鼻子走。在Outlook上设置好邮件规则,按下面的规则把邮件自动分类到不同的邮件夹中:
(1)从直接老板来的,发给你一个人的——马上处理。
(2)从团队成员来的、和项目有关的事情,自动分配到一个叫“Team”的邮件夹中。
(3)从TFS来的状态信息,如团队的check-in email,自动分配到一个叫“Check-In”的邮件夹中。
(4)从公司其他同事来的与工作无关的消息(如笑话,大减价的消息等),自动分配到一个叫“Other”的邮件夹中。
最好每隔两三小时集中阅读和回复一下E-mail。
荔荔:河对岸的河曲数码经常把所有人都拉去做“封闭开发”,这样是否就能避免干扰?
阿超:我们做开发软件,并不是让团队像被关在监狱里一样。要有大家自由交流的时间,团队成员们在无拘束的环境中,会更乐意提问和分享,这会比召开正式会议,强迫每个人分享好得多。在“封闭开发”的情形下,领导也更有可能每天、每小时来询问项目的进度,这种团队内部的干扰源,并不会因为团队搬到一个监狱里而消失。大家还记得MSF的“充分的授权和信任”?
果冻:我这里还有笔记。
小飞:另外,我经常去看测试人员又发现了什么Bug,有时就花时间研究和修复它们。
阿超:可以等第二天会诊之后再看看是否值得马上修复。
果冻:这样新建的Bug要等到第二天门诊之后才能给开发人员处理,是不是会影响进度?
阿超:不一定。
提示:
(1)开发人员在开发阶段最重要的任务是把规定的功能完成!
(2)在项目初期,可以不用集体会诊,开发/测试人员可以直接处理“缺陷”,不必等待。
(3)在任何时候,测试人员都可以把“缺陷”交给开发人员,但是只有会诊的人员才能改变会诊决定。
阿超:我好像有几天没有收到每日构建(Daily Build)的报告了。
小飞:已经有一阵子 Daily Build 没成功了。
阿超:哦?我们上课的时候不是说过“每日构建”的重要性么?
小飞:我同意在我们有时间的情况下,要做每日构建,但是当工作忙的时候,我们的确实没有时间去管构建的问题。
阿超:这么说还是应了那句话——在理论上,理论和实践是一回事,而在实践上,理论和实践是两回事。
阿超指着窗外,河对面的工地。
阿超:他们在建楼房吧。
小飞:对,据说是软件学院的新大楼。
阿超:那,他们的脚手架自从搭好了之后,就没有垮下来过吧。
小飞:那当然不会,俺爹是干这一行的,所有的工人和材料都得运上运下,脚手架要搭得特别结实。
阿超:那你爹他们有没有因为工期紧,就凑合着搭个架子,就往上盖楼?或者脚手架倒了也不管?
小飞:那哪成!要倒下了,就要出人命了,哪还能盖楼?!
阿超:对呀,我们的软件构建,就和脚手架一样,每天都要立着,倒下来就麻烦了。
小飞:不过,我们搞开发的都有点不屑搞构建,没有写程序来劲。
阿超:不会建脚手架的小工,你爹会要么?
小飞:不会。
阿超:不会做构建的程序员,就像不会搭脚手架的小工,运球不熟练的球员。这样的程序员,我们也不要。
小飞:我明白了。
下午,阿超在喝水的时候碰到小飞来报告说Daily Build正在运行中。发现的几个错误都改正了。估计晚上就可以产生一个新的构建了。
阿超:了不起,这么快就做好了。
小飞:超总,我想提一个和我职业发展有关的问题。我们接受的可都是科班的软件工程教育,我们当时的院领导说我们毕业后都是要朝CTO发展的,至少是软件金领,我当然知道这是遥远的将来的事,但是我总觉得我至少可以做一个软件白领吧,这些构建之类的事情,嘿嘿,是不是要由所谓的软件蓝领来做?
阿超:问题提得好……
他望着这位将来的CTO、软件金领,突然想抄起身边的塑料水桶,把水都从他的白领里灌进去。他喝了一大口水,退到窗边,勉强把这个念头压了下去。阿超看着窗外的操场——
阿超:听说你篮球打得不错?
小飞:还行,常和二柱几个玩。
阿超:你提到大学的课程,让我想起大学的篮球课。我们当时考试的科目之一是定点投篮。老师叫每个要考试的同学站在罚球线上,别的同学负责捡球,考生就站着不动,一个一个地瞄准了投,如果进了一个球,老师就开始算分。平时玩球的同学都是十个进七八个,连运动细胞不多的同学都是十个球中五个以上,皆大欢喜,好像大鲨鱼奥尼尔罚球也不过50%上下,大家都有NBA球星的感觉。另一个考试科目是两人配合上篮,当然这是在球场空荡荡的时候进行的,大家接/传/投一气呵成,颇有马龙和斯多克顿的风范,觉得篮球“技止此耳”!考试后,我们意犹未尽,和在一边玩球的同学打球赛,尽管我们“定点投篮”和“二人配合”的分数都很高,但是我们都输得很惨……
小飞:为啥?
阿超:因为我们运球、传球都不熟。这都是我们平时不屑练习的东西,即使勉强到了篮下,投篮都是在踉跄之中完成,当然没有考试时候的准星。
小飞:超总,您把我绕远了,您的意思是?
阿超:我觉得大学的软件工程课,本来要教全面的技术,但是考试往往只考定点投篮和无防守情况下的配合。所以有些大学的高材生到了实际工作中,很多“蓝领”的基本功,比如实战中的运球、传球都不灵,他们津津乐道的定点投篮十中八九的功夫也没有发挥的机会了。我还没有见过从天而降的白领或金领。所谓的大拿们都是从蓝领摸爬滚打出来的。换句话说,我眼里没有白领或蓝领,只有“汗领”——就是大家都得出汗干活。
小飞:你是说构建就像运球、传球……好,我明白了。超总,下班后我们球场见!
头碰头会上,大家发现,最近连续几个构建都不成功,测试组都拿不到新的版本,没法进行测试。
阿超手里拿了小飞整理的“导致构建失败的失误列表”分送到每人手里,表上列举了错误的类型和导致错误的签入,以及牵涉的成员。
大栓:看来拿不到新的构建版本的原因有这些——
(1)构建在开发人员本地机器上就不成功。
(2)构建在本地成功,在服务器上失败。
(3)构建在本地及服务器上成功,但是基本功能不能使用,导致无法进行测试。
阿超:试着对症下药,很多事情我们在培训时都讲过了——
(1)强调基本开发流程(注意编译要产生deBug | release两个版本)。
(2)签入时,必须从TFS同步下载所有最新的版本再编译,而且个人的签入要做成一个Shelveset,成为原子操作,而不能把一次修改中的所有文件分成几次签入。
(3)这时,我们以前做的单元测试和构建验证测试(BVT)就要发挥作用了,每一个开发人员在签入前都要运行所有的单元测试和构建验证测试,确保没有问题后,才能签入。
我们要让团队中做事不仔细的人慢下来,这样能减少他们的危害。将欲取之,必先予之。阿超进而建议——
对于下一个导致构建失败的成员,授予“构建大师”(Build Master)称号,构建大师做下面的事:
(1)负责管理构建服务器。
(2)调试构建,负责找错,并分析出错的原因。
(3)负责把“构建大师”称号和责任交给下一个导致构建失败的成员。
(4)“构建大师”同时向团队的“腐败基金”存入50元,以供大家将来“腐败”之用(此项可选)。
上次头碰头会议后,各个小组都进一步强化了单元测试和BVT。
下午,果冻发了一封E-mail,标题是“我受不了啦!”内容如下——
我的签入流程:
(1)代码写好,本地测试通过,代码复审通过。可以签入了。
(2)同步TFS上最近更新,编译deBug | release,解决版本冲突(半小时)。
(3)安装最新版本,运行本地单元测试,BVT(一个小时)。
(4)提交到TFS上,发现有版本合并冲突,因为在第(2)、(3)步的时候,有人签入了和我的代码有关的新修改。
(5)如果我简单地合并版本,并且签入,很有可能会导致TFS 上编译失败。但是如果我为了保证质量,在合并后,本地编译并运行各种测试,这相当于重复了第(2)、(3)步。当我再次提交签入(重复第(4)步)时,有可能碰到新的版本冲突。这样循环往复以至无穷……
二柱发了E-mail,第一句话就是:
在理论上,理论和实践是一回事,而在实践上,理论和实践是两回事。
然后也抱怨了类似的问题,似乎大部分时间都花在了没有价值的“同步/编译/验证/再同步……”的循环中。
荔荔:似乎应该在签出一个文件的时候,加上一个“防止别人签出”的锁,这样就没有冲突了。
果冻:但是如果你锁住了file1,要签出file2;与此同时,我锁住了file2,要签出file1,这样我们都进入了死锁……
小飞:我刚刚同步TFS,然后编译就不成功了。我在一台全新的机器上重新试了一次,也不行。这至少证明不是我引起的问题。现在我已经没法工作,只好到“顶球”喝两杯去了。谁把错误修好了,就给我发短信,我就回来上班。
阿超:除了构建大师,所有开发人员都可以到“顶球”去玩。
构建大师:我已经连续三天错过了午饭时间,谁能帮我从顶球带两个烧饼回来?另外,能不能不要在午饭前安排构建?要不然铁打的胃也受不了。
经过一个多小时的忙碌,构建终于好了,但是构建大师问阿超,现在构建成功了,明天呢?
阿超:让我想想……
阿超晚上把情况和同事们的意见报告了愚公,阿超在E-mail最后写道:
团队有两条路可以实行:
(1)很严的规则和流程控制,这样会保证很高的签入成功率,如果一个人根据流程来做,几乎肯定能成功。这样构建质量高,但是团队的进展会受到限制。极端情况下,整个团队的进展被序列化为一系列个人串行签入操作。
(2)宽松的规则和流程,每个人随时可以签出签入,签入时的成本很低,但是签入成功率不高,构建质量低,极端情况下,所有人都可以签入、同步,但是没有人能正常工作。
哪一种是最好的呢?
第二天一早,阿超就看到了愚公的回复:
不审势即宽严皆误,从来治蜀要深思。
阿超心想,那什么是我们这个团队目前的“势”呢?他根据每一个步骤,宽、严各是什么做法和当前团队的情况(势),列出了一个“宽”或“严”表,如表14-7所示。
阿超认为,当团队成员的行为只是影响到个人的时候,就尽量放松,让个人根据自己情况处理;当其行为影响到整个团队的时候,就尽量严格,因为整个团队都有可能会受影响。同时,我们要提高可预见性——明确构建大师的职责,公开显示固定的构建时间。
表14-7 宽严表
步骤 | 宽 | 严 | 势 |
签出 | 自由签出 | 签出时候,将文件上锁 | 很多人都会同时编辑同一文件 |
本地单元测试 | 不要求 | 要求 | 每个模块都要求写单元测试 |
本地check-in test(签入测试) | 不要求 | 要求 | BVT还没有完成 |
签入时间 | 任何时候 | 每天固定时间开放 | 目前签入情况很混乱 |
签入冲突处理 | 合并后即可签入 | 合并后,再重新编译,测试,再提交 | 重新测试会花费比较多的时间 |
签入必须经过代码复审 | 随意 | 必须 | 开放人员有一半是新员工,必须通过代码复审建立良好的规范 |
签入时必须运行代码分析工具 | 不要求 | 要求 | 代码分析工具尚未配置好 |
签入时单元测试必须同时签入 | 不要求 | 要求 | 每个模块都要求写单元测试 |
步骤 | 宽 | 严 | 势 |
签入必须是多个相关文件同时签入 | 不要求,可以签入单个文件 | 要求 | 保证每一个签入都不会导致构建失败 |
签入必须和一个工作项关联 | 不要求 | 要求 | 所有的工作必须有工作项跟踪 |
设定专用网络服务,自动处理提交的ShelveSet、构建、BVT、然后签入代码 | 不要求 | 设置 | 需要很多人力来设计并维护 |
续
表
综上所述,移山团队目前的开发流程如表14-8所示。
表14-8 具体流程
时间 | 总体 | 管理/程序 经 理 | 开发 | 构建大师 | 测试 |
8am~10am | 开发人员可以同步代码,这时只有非常要紧的签入才能经过批准,签入TFS | 9am—— 头碰头会议,Bug会诊,分配Bug | 同步代码,构建,根据自己的任务Bug情况决定今天的工作 |
| 测试新版本,新建Bug |
10am~12am | 代码签入时间,经过正常代码复审及其他流程后,代码(连同单元测试)才能签入 | 程序经理组织必要的会议 | 单元测试 | 准备构建服务器 |
|
12am~1pm | 午饭时间 |
|
|
|
|
1pm~3pm | 构建/安装/基本测试/宣布版本质量(见后) |
| 待命,随时准备攻克问题 | 全程监督执行,负责把导致构建失败的缺陷(找人)修复并签入 | 运行基本测试 |
3pm~6pm | 开发/测试人员继续工作 |
|
| 运行BVT后,宣布此次构建质量(失败/可测/可用) | 聪明的测试人员此时就开始测试并报告缺陷 |
6pm | 晚饭及机动时间 |
|
|
|
|
阿超:我特地把紧张的构建及待命阶段安排在午饭之后,这样大家至少可以安心吃饭了。
在这一次的头碰头会议上,平时不主动发言的阿亨也说话了。
阿亨:开发的同志们,你们手里有那么多小强,为什么都揣着掖着,不舍得修复,让测试人员有事情做?测试人员反映因为现有的小强没有被修复,有越来越多的小功能点不能进行测试,我们都要没事做了。
大栓:我们的开发任务很重,必须先把新功能全部实现后,再修复旧的小强。
阿亨:这是不对的,我们有些小强在你们手头很久了,看似举手之劳,为什么不尽快修复,让我们测试组能继续完成测试?
二柱:我们都是按优先级来进行的,开发新功能的优先级远大于修复小强。
阿亨:但是有些开发人员手里头有二三十个小强,难道数量不是一个考虑因素?
阿超:我同意,随着项目的深入,每个人同时要开发新的功能,修复以前的缺陷。由于没有明确的优先次序,一般人都愿意把时间花在开发新功能上。但是我们的确需要平衡进度和质量。有这样的一种方法:
小强地狱(Bug Hell)
如果开发人员的小强(Bug)数量超过一规定值,则此君被送入“小强地狱”,在地狱中,他能做的唯一一件事就是修复小强,直到小强数量低于此阈值。
这一阈值由团队根据实际情况来确定,要注意:开发人员同时“入狱”的人数应在全体成员的5%~30%之间,若比例太高,则要考虑阈值或小强数量的计算方式是否合理(是否只包括某一严重程度以上的Bug)。
在项目过程中,阈值不宜频繁调整,最好事先宣布阈值。
大栓:但是我们已经违反了“最好事先宣布阈值”这一规定。
阿超:如果我们现在要让20% 的同志入狱,需要马上运行一个TFS 查询看看这一个阈值是多少?是15?好,那我们现在宣布给大家三天时间,第三天后,小强数量达到或超过15 的开发人员请入狱,然后每天早上9点头碰头会议时统计并宣布入狱/出狱名单。
大牛: 其实先把所有的功能写完也不错,至少我可以告诉客户“功能写完了”,让他们高兴高兴。
大栓:大牛,这不就是咱们以前项目的情况么?你一直问“功能都写完了,为什么还不能用”? 我们一直说“还有一些小问题”,然后小问题总是不能解决,因为要真正解决这些“小问题”的话,我们还得重写一些功能。
阿超:对,很多问题,甚至是大问题,都隐藏在目前的小强后面,如果一味赶所谓的“进度”,到时候有些小强就变成了大怪物,因为我们已经在错误的基础上搭建了很多新的逻辑和功能,这时再来处理一些历久弥新的小强,就有投鼠忌器的麻烦。
阿亨:那怎么办?
阿超:我们要分析小强,看看这是一个小问题,解决了就万事大吉呢?还是冰山的一角,解决后也许会发现更多、更棘手的问题。或者看似不经意的一个小强会让很多人加班重新实现功能——这就成了设计变更需求——DCR。
14.6.6 DCR Tell mode v.s. Ask mode设计变更
在项目早期,如果大家觉得要做一个设计变更,可以用告知模式(Tell-mode)的形式,就是说,修改方必须通告所有关系人:“我在这里修改了某某界面。”但是修改方不必取得其他关系人(或者模块)的事先同意,就是说可以先行设计并编码。当然,如果其他关系人不同意,修改还是不能签入。
当项目进行到稳定阶段,Tell-mode 要改为请求模式(Ask-mode),这时,修改方必须先问“我是否可以在这里修改某某界面?”(当然还要有更详尽和充分的理由)得到肯定的答复后,才能进行修改。这时的默认回答是“不”。
头碰头会议。
大栓:我有一点不太放心……
大牛:哦,为啥?我们大家都干得很欢。完成任务应该没问题吧。
大栓:我们每天都在签入新的代码,每人都很忙,但是我总觉得不太对劲。会不会越做事情越多呢?
阿超:这时我们可以看看各种报表,首要推荐的是“Remaining Work”。
请看图14-2。
这个报表告诉我们究竟还剩下多少事情要做。我们要分析这么多签入到底是否解决(Resolve)了相应的问题,如果是,那么我们的任务数量应该逐渐下降,但是从图14-2上我们看到深灰色的任务和缺陷还在缓慢增长。而浅灰色的“已完成任务”,却一动不动,这就要引起我们的注意了。
可以在报表设置控制板中,进一步选择你要报告的内容,如:Iteration,选择里程碑;Area,选择项目的不同部分,也可以修改报告的起始和终止日期等。
[注: VS2010 还提供了 burndown chart 等, 参加中科大同学的博客, 例如
http://www.cnblogs.com/OMG-Team/archive/2011/09/30/2196150.html]
什么叫代码完成(Code Complete[1])?
就是我们认为所有应该写的代码都写了,所有应该实现的功能都实现了。
那就是可以发布了?
不,代码都写了。但是代码中可能有很多小强,各个模块之间的合作还有很多问题。Beta用户看到产品后,说不定要提不少修改意见。但是,我们毕竟是把“我们认为所有应该写的代码都写了,功能都实现了”。这是一个了不起的事件。一个团队经过几个月的努力,从无到有,从简到繁,把几个月前的远景变成了可以运行的软件,也许我们还有许多问题,但这无疑是很值得庆祝的!
在TFS上,就是所有的代码任务(Task)都完成了。也许我们现在还有许多缺陷(Bug),还有一些与测试相关的任务。这些事情要留到以后稳定阶段才能全部解决。
14.8 讨论
问: 每次里程碑结束后,我们向客户汇报的时候,客户总是会惊讶地说,某某功能不是我们当初商量的那样啊,而PM却也同样一脸诧异地说,不对啊,当时咱们就是这么说好的啊,有文档为证。客户不干了,威胁不加/不改xx功能就如何如何,这时PM该怎么办?
大牛:我们在合同里要写明到底我们要交付的是什么,这就要看PM的分析和说明能力了。有时要对客户说“不”。同时,我们在需求说明中也要从用户的角度去描述问题和解决方案,这样用户才能了解他们最终会得到什么。
问: 项目开发中后期,Dev lead用工具一统计,乖乖,足足xx万行代码,xx千个存储过程,可是每到给客户演示时,却不时出现程序的各个功能相互不配合,不能自圆其说的尴尬场景,Dev lead很郁闷,想想自己可是没少加班啊,代码量也够多,可是问题究竟出在什么方面呢?
阿超:一个原因是每个人都沉浸在“我要写出最强大的某某类或某某模块”,不停地优化一些没有人用的功能,但是真正能够为其他模块使用的功能却未能实现。他们忘了他们写的代码是给别人用的,而且是为了解决用户问题的。所以这个时候我们要想想“用场景驱动”的方法,保证典型的用户场景能够实现。如果从“场景”出发,各个模块的互相集成就能得到充分的测试,按照场景演示起来就更有保障了。
问: 在项目开始之前, 有很多队员还没有接触过编程语言(例如C#),导致PM在分配任务时很难用时间来衡量,就拿写一个Web Service这一模块来说,一个熟练的程序员可能只需要两个小时,而对于C#的初学者来说,就得先花两天来理解Web Service的实现机制和原理。在有限时间的催促下,导致一些紧急的任务不断向高手集中,而初学者的任务越来越少。这时应该怎么办?
阿超:对于这些队员,可以考虑在他们自己的任务估计值之上再乘以4。另外,如果你是写一个商业项目,请不要让连开发语言都没有接触过的队员进行开发工作。并不是非得 “写” 程序才是对项目有贡献,有时不写也有很好的贡献。如果他们有热情,就从测试开始学习吧。请参看前面提到的“大马哈鱼洄游模型”。