敏捷无它,唯持续改进
一、背景介绍
这个案例发生在我曾经所在的C公司,我是两进两出这个公司,这个案例主要是讲述我第二次进这个公司后一年之中所发生的一些事情,以及我的一些思考。
那一年,距离公司引入Scrum已经将近3年,喧嚣过后,Scrum和敏捷何去何从?公司经过3年变得敏捷了吗?敏捷的本质又是什么?
二、我与敏捷的第一次亲密接触
2006年我第一次进C公司。这个公司有好几条产品线,做的也都是企业应用软件开发,在上海和南京均有研发部门,而我是一直在上海工作的。
2007年的时候,公司的VP开始在我们团队推广Scrum,现在想起来,在国内应该算比较早的了。那时候除了那位VP,其他人基本都没听说过Scrum这个东西,而公司的研发团队不少,所以刚开始并没有很全面地采用Scrum的所有实践。像我们团队,所使用的实践首先是迭代、还有估算、每日站立会议,基本就这些了。
我记得我们的前几个迭代是这样的。
我们挑出要完成的一些特性集合,然后估算出需要3周还是4周来完成,定出这个迭代的周期(也就是说我们的迭代并不是时间固定的)。我们的估算,以0.5人 天为最小单位,每个人独立估算,然后算出平均值就是最后的结果。听上去过于简单?后来有了改进版,那些估算比较准确的人,在下一个迭代的估算中会增加权 重,而不准确的人会减少权重。这样的估算机制,现在想起来,还是很准确的,我们的迭代基本上都在估算的时间内结束,偶尔也有失败的时候,我们就延长迭代!
每个迭代还会选出一个领导(由几个资深程序员轮流担任),这个领导的任务是尽量保证迭代完成,然后在迭代结束时向PO来做演示,有点ScrumMaster的意思,可大家都对Scrum不熟,所以也不能算。其实那时根本没有ScrumMaster的提法。
迭代开始后,由这个领导对要完成的特性(还没有Story的概念)进行优先级(High Level)的设计,一些重要类的实现,如果有必要,还会画出UML图。然后分解出所有要做的任务,分配给组员。因为我们早就有了日构建(Daily Build),所以领导还需要负责隔三差五地检验各个日构建是不是被成功集成起来,因为在迭代的最后一天,他需要向PO 演示所有的特性。
迭代刚开始一两天领导在做优先级(High Level)设计的时候,其他人在干什么呢?在修复一些Bug,或者上个迭代里PO的一些反馈。
由于我们的迭代基本上都是一个月,加上那个VP不久就调到南京办公室去推敏捷了,我们并没有经历很多迭代。所以后面也没有精彩的故事发生。
基本就这样了,没有白板,没有用户故事,没有ScrumMaster,没有回顾会议……用现在的眼光来看,它是彻彻底底的伪Scrum或者Scrum-but,每个迭代里都是一个瀑布式的过程。那么,它是敏捷吗?
三、敏捷原来是这样的
2009年初的时候我离开了C公司,加入了Daniel Teng(http://www.danielteng.com/)所领导的一个不大的公司,在里面工作了10个月(10个月后我回到C公司,加入另一个部门,历时1年,就是本案例所要讲述的1年)。这10个月给我的影响非常大,身临其境了才知道,原来敏捷是这样的。
首先公司的布局“杂乱无章”,办公桌被摆成了“骨头”形状,每块骨头可以坐六个人,这六个人基本上就是抬头即可看到对方,整个办公室就是六七块骨头组成的。您猜对了,每块骨头的那六个人,就是一个Scrum团队。
办公室的门口摆着一个大白板纸,上面一张白纸上是手写的当月的日历,我上班第一天看到日历上的当天写着“John Cai on board”,John Cai就是我了。后面有些天写着“某某某请假一天”。这是干啥的?这是Informative Workshop的一部分。
旁边的墙上一个大的液晶显示屏,上面绿绿的一些英文字,这是CI(持续集成)墙。如果谁的代码签入引起CI红了,不仅立刻显示在液晶屏上,还会有声音发出来,全公司都听得到,这时就会有人去修复,这个人的名字也会在液晶屏上显示。当然这不是液晶屏的唯一功能,下班后,它就是用来玩Wii的了。
我记得上班第一天,Daniel问我吃早饭没有,说厨房有牛奶、饮料、各种零食。我刚开始以为是周一福利,但其实在我待的10个月里,零食就没断过。每周一、三、五另外增加水果时间,所有人轮流负责买水果,洗水果,然后发到每个人的桌上。
很多书,敏捷的、技术的,所有跟工作相关的,如果这些还不够,只管自己去买就行了,买回来放公司还是放家随你,一律公司报销,不需审批,完全自主。
每个人都在讨论和实践着Pair、TDD等工程实践。各种新技术也是茶余饭后的谈资。
每周五中午都有Tech Talk,公司买来中午饭,除了主讲人需要提前吃饭,其他人手里拿着鸡腿、比萨或者盒饭,边吃边听。
每个月每个人都有固定的团队建设费,一般一个Scrum团队的人会把这些钱合在一起,自行组织出去活动,我就是用这样的机会和团队一起看了电影《2012》。
……
在这样的环境中过了10个月,原来C公司的主管让我回去,说实话,到那个时候为止,我在这个公司所获得的大多数是感官认识,有很多事情知道了他们是这么做的,至于为什么要这么做,这么做背后的价值观、原理,还是缺乏更深的体会。
原公司主管叫我回去做技术主管,“技术上带带公司一个比较年轻的团队”。可是我心里想的不止是技术上“带带”,我想把这里学到的关于一切敏捷的好东西,都带回去。
四、第一个挑战
2010年初,我回到了C公司,加入了另一个部门。因为原先的主管换到这个部门做老大了,这个部门是做人力资源管理系统的(以下简称HRP),由3个Scrum团队组成,其中两个在上海,另一个在南京。上海的两个团队很多都是开发这个HRP产品的元老,而南京的那个团队,相对比较年轻,也没有元老级的人物(事实上南京办公室本来就是晚于上海办公室建立的),技术上相对薄弱一些,主管叫我回来就是技术上带一下这个南京团队。
这个团队由5个开发、1个PO、2个QA组成,开发都是男的,其余都是女的,是个很合理、很标准的Scrum团队。而且都比较年轻,朝气蓬勃,自信心十足——这就是我面临的第一个挑战。
我心 里的目标很明确:要提高这个团队。如果是一个自知有很多问题的团队,比较好办,因为它已经是接纳的心态。可是面对一群自信心十足的年轻人,他们认为一切都 很不错,我怎么去提高他们?首先我不能直接告诉他们:你们是需要提高的。他们对我这个空降的技术主管本身就心存芥蒂,有些若隐若现的抵触情绪。于是我决定 要做的第一件事是让他们认可我。
五、团队的好奇心
我用的方法很简单,是从Daniel Teng身上学到的:我每周会从我读过的好的博客文章中选出5~10篇,以每周链接(Weekly Links)的形式分享给他们。邮件里特别注明了是分享,没有强迫他们读的意思,就是同事间的平等分享而已。
这种小小的举动其实带来很多好的东西:
- 我做出了表率,他们心里清楚,我要每周挑出几篇来分享,我读的肯定至少是几十篇。这种表率对建立一种学习氛围是很重要的。
- 我发的基本都是英文博客,很多技术和好的想法都是从国外源起的,很多国外博客确实质量高得多,相对比较有权威性,同时这是对他们的一个暗示:技术人员要尽量接触国外第一手资料。
- 这是 一种很好的开阔眼界。既然是英文博客,我相信即使我发给他们,他们不一定都看,但我尽量挑一些对他们来说比较陌生的、可是又很重要的话题。他们光看看标题 也是有收获的,至少打开了他们的视野。对于一个比较封闭的团队(我相信很多没有学习氛围的团队都是这样),打开一扇窗,吹进去一缕风,就是个很好的开端。
- 由于我分享的Lean、NoSql、DDD、TDD、Pair Programming,OO的SOLID原则等,这些都是这个年轻的团队几乎听都没听过的概念,一个很重要的东西被激发了:好奇心。
- 有了好奇心,这个团队就处于“可被激活”的状态了。
六、更多挑战
我的聚焦点自然还是技术方面,慢慢观察下来,情况很不乐观:
- 3个Scrum团队各自负责开发几个独立的模块,这是正常的,但他们各自都有一个独立的.NET solution文件,每个组的solution文件都只包含他们所负责模块的project。也就是说他们工作在不同的codebase上。这样团队就没有交流,我是指没有代码层面的交流。
- 使用微软的VSS作 为源代码管理工具。服务器在上海,南京获取代码奇慢无比,要好几个小时。于是他们是这么运作的:每周一由南京某同事花几小时时间把最新代码获取下来,然后 复制给南京其他同事,这一周,南京团队就在这个“最新”代码上工作了,只签入,不再获取。什么?这样容易有代码冲突?没事,他们使用——写到这里我决定要 融入他们团队了,以下不再使用“他们”,而是“我们”——我们使用独占式检查方式,保证每个人的工作都没有交集,也就没有冲突。这让信奉CI的我情何以堪?
- 南京团队对技术架构没什么发言权,上海定好了约定俗成的架构,南京照做就是了,他们按部就班地照着架构干活:ASP.NET WebForm page的code behind文件调用service,比如AttendanceService.GetAttendance()方法,而AttendanceService.Get Attendance()什么也不做,只是调用IDAO.GetAttendance()方法,IDAO的方法里就是SQL了。于是我看到一大堆什么也没做的Servcie(还有他们的接口),而业务逻辑很多都表达在SQL语句里。
- 没 有单元测试,可是有些成员会告诉我有。因为的确有若干自动化测试,可是那些自动化测试都必须连接数据库才能运行,运行得慢,所以很少运行,日构建只是构 建,没有运行这些自动化测试。我试着运行几个,可能由于数据库里的数据已经变了,所以失败了,我之所以说“可能”,是因为我没有深究,没法深究,自动化测 试的代码也是一大段,看也看不懂,不知道它想测什么。
- Sprint(迭代)不能按时结束。上海团队是有QA Sprint的,也就是QA的Sprint落后开发的Sprint一个周期,他们在Sprint内测试开发人员上个Sprint开发完成的东西。
- 技术上,大部分程序员其实都没有什么面向对象设计能力,面向过程式的代码到处可见——这即使在其他公司,也是很普遍的现象。
……
七、团队的惯性
现在回过头来想,我那时看到的那些问题,难道没有人看到吗?我相信不会,尤其是上海团队中有几位还是很不错的,可是为什么那些问题一直存在着呢?
我想这就是一个团队的惯性,或者一个团队的文化。当日复一日地按着团队的惯性做事时,我们每个人就不知不觉地被“体制化”了。
而我现在觉得,主管的一个作用就是要设法建立起这样的一个机制,让团队从惯性中挣脱出来,一个办法就是自己走在前面,让别人愿意跟随——这就是主管(Lead)和经理(Manager)的一个区别,Manager是“让别人跟随”,少了“愿意”二字。
可是怎么培养自己的领导力(Leadership)?我当时并没有多想,我着眼于怎么取得南京团队的信任,让他们愿意跟着我的思路去做一些改变。
八、镜子
陷入惯性的团队往往自我感觉良好,或者至少对一些问题都习以为常见怪不怪了。这时我能做的一点就是给团队一面镜子,让它看到自己的不足。就如同我们每天出 门前都要照照镜子,看看脸上有没有饭粒,发型会不会很怪,而且这面镜子还必须是高清的,最好是任何一点瑕疵都能看得一清二楚。团队同样如此。
我给南京团队的第一面镜子是一个代码分析工具,叫NDepend,这是一款.NET下检查各种代码指标的工具,比如圈复杂度、包的稳定性和依赖性分析,还有类和方法的代码行数统计之类的。南京团队一个同事之前跟我说:他们几年前的代码比较差,但现在的模块采用了新的架构(就是Service啥也没做,直接调用数据访问层那一套),已经比较好了,言下之意颇为自得的样子。
我就先用NDepend分析了一下他们新的模块代码,告诉他们圈复杂度有点高,类有点大,同时在那周的Weekly Link里,我着重分享了OO里的几个设计原则,“单一职责原则”、“依赖倒置原则”之类的,又提到了单元测试的几个原则,比如不能连接数据库,而他们的代码要想测试,没有办法不连数据库。
这些镜子照出来的瑕疵,为我的下一步计划提供了很好的切入点。那就是“单元测试”。
九、从一开始就要高标准
我在后来读到过一篇文章,讲的是大家总结下来的敏捷转型的十大建议,其中一个建议就是“从一开始就要高标准并且一直保持”,所谓高标准并不是要求团队一口吃成胖子,一下就达到高标准的要求,而是对团队一开始就设置高期望值,让团队拥有持续改进的动力和一个愿景。
无独有偶,我前一阵有一天在公司翻到一本关于英语教学的书,里面第一章的标题就是“Setting High Academic Expectations”,第一句话是“high expectations are the most reliable driver of high student achievement(高期望值是带给学生高成就的最可靠的驱动力)”,教学如此,敏捷也是如此。
因此我想推单元测试,我一下就给他们来个高标准,就是TDD。
十、TDD的争议
TDD在网上是有争议的,有人视之为一个团队的必须实践,也有人认为它太浪费时间。我把它当作一个高级程序员和初级程序员的分水岭。我到现在仍然认为:至少在企业应用软件行业(由于我本人毕业至今做的都是企业应用软件,从没涉猎其他领域,所以对其他领域不妄言),一个没有掌握TDD的程序员,就是初级程序员,不论他技术如何。
也许有人不以为然,认为有不少技术很好的程序员也不写单元测试,更不会TDD。可是仔细想想:这种人所谓技术很好,是他写的代码很有可读性?还是他只是对某些框架、工具或控件非常熟悉。
为什么我提到“可读性”?因为一个企业应用软件,它的维护成本往往是开发成本的十几倍乃至数十倍以上(只有那些可读性非常好的软件,这种比例能在十倍以 内),再考虑到我们每天在现有的代码库上写代码,都要先读读原来的代码。也就是说,一个团队花在读代码上的时间往往是写代码的时间的百倍以上,一点不夸 张。所以一个好的程序员,写出可读性、维护性好的代码,比解决一个技术难题重要多了,而且说实话,在企业应用软件开发中,真没有什么很难搞定的技术问题, 最难的是复杂无比又千变万化的业务。
为什么我在说TDD的时候提到“可读性”?网上有些反对TDD的人把TDD当做测试来说事儿,真是令人失望。这些人不知道TDD首先不是关于“测试”的,它是关于“设计”的。这句话我经常在同事面前说,但同时我也知道,一个没有尝试或掌握TDD的人,不会理解这句话。
本文节选自《敏捷开发一千零一夜》一书,王立杰 主编,电子工业出版社出版