• Agile-forum-beijing-2018
  • Agile-forum-shanghai-2018
  • Scrum@scale-2
  • A-csm
  • Cal1807
  • Shine-scrum-02
  • Arne-2018
  • Jim-banner
  • Cams2
  • Cal
  • Inner-training
  • Growth-process
  • Jens-heart-of-scrum
鬼节真故事——欠债总是要还的(技术债务上篇)

技术债 故事篇


如期完工


几年前,S公司总有一些项目不能如期完工,Z君的项目也是其一。今年,新总经理上任,在公司级别的项目管理会议上,Z君向总经理解释了不能如期完工的原因。当着项目经理的面,总经理明确提出:“项目不能如期完工,任何解释都不接受。”散会了,Z君翻了个白眼,边走边嘟囔:“连解释都不让解释了,UX不断变更的问题,依赖的组件不稳定的问题,这些又不是我能控制的……”

几天后的一个早上,Z君的部门经理笑呵呵地走到Z君身旁,指着Z君电脑屏幕上刚打开的电子邮件箱说:“公司颁布了项目奖金评比制度,规定不能如期完工,奖金全部扣光。怎么样?加油啊!”Z君吃了一惊,仔细看看奖金数额,想起了新婚的妻子,要还的房贷……


固定的时间和范围、对不齐进度的组件团队、没有自动化的测试


S公司是典型的由组件团队组成的组织,UX团队、负责不同组件的若干开发团队、测试团队,在产品经理的协调下协作完成项目。Z君与产品经理保持着密切的联系,月月有计划,周周有报告。眼看进入量产季,Z君月初就给项目经理发邮局询问若干事项。产品经理很快回信了。


Z君,你好!

来信问到的问题答复如下:

1.软件版本发布截止日期:2014-10-30。请严格遵守,不能延迟

2.本次版本需要的8个UX文档,UX团队说月初提供其中的6个,其余2个月中才有。

3.你项目应用程序所依赖的中间件,其负责团队月中能够提供更新好的版本。

4.我们要在2014-10-30前完成代码集成,准时启动系统测试。

                                                                  2014-10-02


Z君看后,找来各个模块的开发者进行商讨,然后给产品经理又写一封邮件:


C君,你好!

感想分享信息,我们会尽力如期发布。但对在月中才发布的UX文档,我们担心时间太紧,来不及用代码实现。这两个应用都需要2周才能写好代码,还需要1周开发者自己测试以确保发布质量。能否请您帮助说明,请UX部门早一点提供UX文档呢?

                                                                     2014-10-02


第二天,产品经理回信:


Z君,你好!

发布范围不可缩小。

UX团队我已经催过,但他们说他们正忙于一个优先级更高的项目。

中间件团队查看了你提出的变更邀请,他们说这样变更会导致大量代码修改,影响平台稳定,建议你们在应用层客服困难,实现这个特性。

                                                        

                                                                      2014-10-16


2014-10-23日,Z君收到了第7个模块的UX文档,

2014-10-29日,Z君终于收到了第8个模块的UX文档。

Z君和第8个模块的开发者奋战了一个通宵,终于在2014-10-30早上提交了代码。

Z君轻声问开发者小M:“自己测了吗?感觉质量怎么样?”

小M揉着布满血丝的眼睛,哑着嗓子说:“测试?我从昨天下午忙到现在,连口水都没喝。”


还债的日子


每个版本测试团队都测出大量bug。Z君的团队除了要按期完成新特性的开发,还要修改上次发布中存在的bug。除了加班,Z君实在没有其他的法子。于是他们晚上加班,周末加班,每月团队人均加班100小时。十一月、十二月很快过去,到元旦放假,Z君想:“不管怎样,先喘一口气儿吧。”2015年的第一天,Z君早上睡了个懒觉,正准备吃中饭,突然手机响了起来。

“Z君,测试团队报了400个bug,要求今天解完。”电话那头是部门经理的声音,“CH总也在,对这个项目非常关注。”

Z君心里一抖:“好,我马上来。”

部门经理说:“量产很关键,大家辛苦,但工作要做完,不要急着回家。”于是Z君大年三十晚上加班,干到9点,春节从初一到初七天天加班。春节期间没有外卖,Z君就在自己的座位后面堆满饼干和泡面充饥。

一边开发新特性,一边修bug,夜以继日,加班加点的日子一旦开始就好像掉进一个坑,越挣扎陷得越深,怎么也看不到希望。


客户投诉


该来的躲也躲不开,客户投诉了,说Z君团队负责的产品响应客户变更需求的速度慢,质量问题多,表示很失望,不再愿意继续业务合作关系。


领导批评


Z君满腹委屈,写了项目的回顾报告,分析bug产生的原因,加班的工作量,列举大量的数据,如实反映困难所在,却不敢有一句抱怨。部门经理、产品经理应邀参加项目回顾会议。

产品经理听完Z君的报告,严肃地说:“你这个报告是什么态度?!你的意思是说,从头到尾,你只是个受害者,对不对?UX文档的问题,依赖组件的问题,都是别人的问题,对不对?你没有问题吗?你应该深刻反省你自己的问题!”

Z君惊讶到目瞪口呆,而后低头不语。

部门经理接着说:“每个部门都要讲业绩,每个团队都要为提升业绩做出自己的贡献。你说说,你们团队这一年来有什么贡献?”

“我们能活到现在就是贡献!”Z君怒吼出来,脸色发白,嘴唇颤抖。


员工辞职


Z君团队成员陆续辞职了,在项目管理系统中,这个项目的成员名单80%被标记为红色(已离职)。Z君的妻子两次流产,人们说,说因为Z君太辛苦了。Z君请了6个月病假,在家休养,期间妻子第三次怀孕。后来,Z君也辞职了,同事们看到他晒在朋友圈里的幸福照片:抱着新生儿和妻子在一起。


技术债  感想篇


学习《Essential Scrum》第八章技术债有两方面收获,首先学习Ken是怎样分析问题、解决问题的,然后思考我们怎样运用学到的知识解决自己工作中的问题。


Ken是怎样分析问题、解决问题的


技术债的分类


对于技术债的分类,敏捷大师们有很多讨论,也有各自不同的分类方法。Ken提出技术债的三种主要形式(幼稚的技术债、不可避免的技术债和策略性技术债),并分析它们各自产生的根本原因。结合Ken发表在博客上的文章,可以看出,幼稚的技术债源于“团队成员不成熟”,不可避免的技术债源于“无法完美预测未来”,策略性技术债源于“组织有意采取的策略”。


问题分析清了,根本原因找到了,解决方案自然呈现出来。


既然幼稚的技术债源于团队成员的不成熟,那么根源在开发团队身上。开发者粗心大意、不负责任,那就给团队成员提供技术实践方面的培训,在完成的定义中给团队细化、明确在技术方面的要求,培养团队成员,帮助团队成员成熟起来,从而达到停止增加技术债的目的。


不可避免的技术债源于无法完美预测未来,根源在于开发创新型产品时,人们对业务领域的认知规律天然如此。我们无法改变这个认知规律,但可以运用这个规律。Scrum通过短期频繁迭代,快速获得对业务领域的认知,来减少负债。


既然策略性技术债是组织在权衡利弊后有意采取的策略,那么根源在组织身上。重写代码需要多少时间?相当于提高多少成本?避免或承担技术债,哪个收益更高?技术债可以作为一个工具,量化部分成本,帮助决策者认识到技术债的利弊,正确决策。


用我自己的话总结一下Ken的思路:不该欠的债,不欠;不得不欠的债,少欠;不确定要不要欠的债,先量化比较再做决策。


技术债的管理力度


技术债的管理力度,弹性相当大。Ken指出,技术债像财务债一样,必须加以管理。同时他又指出,没有哪个产品能做到“无债一身轻”。对于这样一个必须要管,又不能管死的问题,如何掌握弹性空间呢?这里,我们能体会到充分且必要的管理原则。Ken以不明显影响将来产品开发为准,为技术债积累设定了上线阈值。管理要充分,技术债积累不能超过上限阈值,否则必将对产品的后继开发明显不利。但是否要努力达到零负债呢?不必。因为那样做,组织的经济效益未必是最大化的。这个道理容易懂。以买房做类比,当下房价疯涨,攒钱不买房,一年又白忙。你需要买房,是攒够钱再买呢?还是贷款负债买房呢?只要把月供控制在可承受范围内,当然贷款买房的经济效益是最大的。这个可承受范围就是合理的弹性空间。


技术债的偿还原则


Ken提出了偿还技术债的5条原则。这些原则至少基于2类基础,一类是其他人的研究成果,另一类,也是给我们读者印象最深刻的一类,是Ken自己的实践。


比如,第一条原则是“并非所有技术债都应该偿还”。Ken生动地重现了一段与某集团副总裁交涉的亲身经历。听听副总裁是怎么对Ken说的:“孩子,哪怕你花我一个子儿去清理系统,我也会亲自把你逮起来毙掉。”如果当时被喷的是你或我,这是多么尴尬的记忆。或许我们会像很多人那样,抱怨不被理解,然后不了了之。但我们看Ken是怎样反省自己的。Ken说:“从我个人的角度来看,上线一个脆弱的系统对他们来说存在巨大的风险,但从利润的角度来看,我的想法显然错了。”有了换位思考,有了多角度的观察和理解一个问题,才有了“并非所有技术债都应该偿还”这样一条重要的原则。


又比如,第三条原则“分期偿还技术债”。在这部分,Ken总结了他指导团队实施Scrum时观察到的常见错误:“技术债冲刺”,并指出该错误导致的不良后果:技术债冲刺是一个误导,它让人们以为,债务可以增长,不用刻意减少。于是本该在每个冲刺完成的工作,却堆积到不得不处理了再专门花一个冲刺来处理。Ken指出技术债冲刺最严重的后果是影响交付客户价值,进而提出“分期偿还”的原则。


再比如,最后一条原则“一边做有客户价值的工作,一边偿还相应部分的技术债”。意思是,比如在开发某个有客户价值特性的时候,涉及到几个C++文件的修改。那么,对于这几个C++文件而言,首先保证不添加幼稚的技术债,其次清除偶然发现的技术债,最后,也是最核心的,专门偿还出现在这几个文件中的技术债。这个办法简单、自然,又能兼顾多方面需求,如PO关注交付客户价值,团队成员关注偿还责任等。如此巧妙的方法,出自人民群众的智慧,即Scrum团队的智慧。Ken观察到这些优秀的实践,再把它总结提炼成为原则。


Ken善于在实施Scrum的实践中总结,既有成功经验,又有失败的教训。于是关于偿还技术债,我们应该怎样做,避免怎样做的原则被提炼出来,而这些原则进而不断丰富着、发展着Scrum理论。这些原则于读者来说,如醍醐灌顶,让人受益匪浅。


解决自己工作中的问题


我在敏捷教练的实践中,遇到过技术债管理的两类典型错误。


不了解不可避免的技术债


某组织有独立的测试团队执行系统测试,把缺陷登录在缺陷管理系统中。在管理者看来,修复这些缺陷需要花费开发团队大量时间,在质量保证部门看来,这些缺陷意味着软件质量差。于是组织提出“预防缺陷”的口号,试图通过更频繁地监控缺陷密度(关闭的缺陷个数/代码行数),强化代码审查、单元测试等活动,显著减少测试部门发现的缺陷,最终达到节约时间,提高质量的目的。但事实证明,在强化对开发团队监督以后,缺陷个数并未显著减少。原因何在?


分析发现,大量缺陷无法预防。例如,UX规格书在频繁迭代过程中根据反馈意见不断修改,测试团队根据最新版UX测试旧版软件,登录缺陷,促进开发团队根据UX规格书更新软件,这就是不可避免的技术债。又例如,一个开发团队开发的组件A依赖于第三方开发的组件B。当组件B变更接口时,测试团队测试包含新版A和旧版B的现有软件系统,登录缺陷,促进开发团队更新组件A,这也是不可避免的技术债。迭代次数越多,得到的反馈信息越多,变更就越多,不可避免的技术债就越多。多次迭代的缺陷累计总数与迭代次数有正比例关系,随迭代次数的增长近似线性增长。经过多个迭代周期,缺陷管理系统上登记的缺陷中,由不可避免的技术债导致的缺陷数量几乎淹没了幼稚的技术债导致的缺陷数量。所以,无论怎样强化监督,缺陷数目也无法显著减少。也就是说,对于使用迭代开发的创新型产品来说,技术开发团队百分之百地认真开发,不出任何粗心大意的错误,缺陷密度这个KPI也不会有明显下降。


认识到不可避免的技术债天然无法“预防”这个道理后,管理思路也需要相应改变。不可避免的技术债,管理重点在于及时偿还。缺陷密度这个KPI以关闭的缺陷作分子,只能提示多少技术债已经得到偿还,但更应关注的是,等待偿还的技术债是否超过了上限阈值。依据一个团队的开发能力,合理设定其上限阈值,然后监督尚未偿还的技术债的累计情况,低于阈值是正常情况,高于阈值才需要提高关注。以房贷类比,已还多少贷款不重要,重要的是还有多少贷款没还,更重要的是,这个月的贷款是否高到还不起的地步了。每个月需要还房贷不是问题,及时还贷是正常情况,这个月没钱还贷才是需要警惕的异常情况。


忽视幼稚的技术债


幼稚的技术债是可以避免的,但不成熟的开发团队常常任由其积累,不注意偿还。


实际开发工作中,不成熟的开发团队和团队中不成熟的开发人员并不少见,他们写完代码就提交,单元测试敷衍了事。只是给开发团队提供培训,告诉他们要运用测试驱动开发、要注意重构代码,要制定并执行“strong definition of done”,提醒他们定期检查,甚至定期审计他们的技术实践活动,这些工作往往花费人力不少,但收效甚微。


经过长期实践,我们摸索出一套自动化触发模式。原理是依托于一套自动化构建系统,以代码提交作为触发条件,触发若干项活动自动进行,如单元测试、全局变量统计分析、圈复杂度统计分析,内存碎片分析等。每项活动进行完毕,构建系统自动在缺陷管理系统中登录相应问题,并用特殊标记标识,促进开发团队解决。以代码提交作为触发条件,把开发团队编写代码的活动与检查代码的活动自然地结合在一起,不成熟的开发者再也不会忘记,也无法敷衍这些活动,从而使技术实践落到实处。


   本文作者书山有伴 ,由ShineScrum审核