1. Blogs of Wangbaoqiang首页
  2. 读书

关于敏捷的思考

摘自《敏捷开发一千零一夜》

一、背景介绍


这个案例发生在我曾经所在的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 Caion 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、PairProgramming,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(迭代)不能按时结束。上海团队是有QASprint的,也就是QA的Sprint落后开发的Sprint一个周期,他们在Sprint内测试开发人员上个Sprint开发完成的东西。

● 技术上,大部分程序员其实都没有什么面向对象设计能力,面向过程式的代码到处可见——这即使在其他公司,也是很普遍的现象。……

七、团队的惯性


现在回过头来想,我那时看到的那些问题,难道没有人看到吗?我相信不会,尤其是上海团队中有几位还是很不错的,可是为什么那些问题一直存在着呢?我想这就是一个团队的惯性,或者一个团队的文化。当日复一日地按着团队的惯性做事时,我们每个人就不知不觉地被“体制化”了。而我现在觉得,主管的一个作用就是要设法建立起这样的一个机制,让团队从惯性中挣脱出来,一个办法就是自己走在前面,让别人愿意跟随——这就是主管(Lead)和经理(Manager)的一个区别,Manager是“让别人跟随”,少了“愿意”二字。可是怎么培养自己的领导力(Leadership)?我当时并没有多想,我着眼于怎么取得南京团队的信任,让他们愿意跟着我的思路去做一些改变。

八、镜子


陷入惯性的团队往往自我感觉良好,或者至少对一些问题都习以为常见怪不怪了。这时我能做的一点就是给团队一面镜子,让它看到自己的不足。就如同我们每天出门前都要照照镜子,看看脸上有没有饭粒,发型会不会很怪,而且这面镜子还必须是高清的,最好是任何一点瑕疵都能看得一清二楚。团队同样如此。我给南京团队的第一面镜子是一个代码分析工具,叫NDepend,这是一款.NET下检查各种代码指标的工具,比如圈复杂度、包的稳定性和依赖性分析,还有类和方法的代码行数统计之类的。南京团队一个同事之前跟我说:他们几年前的代码比较差,但现在的模块采用了新的架构(就是Service啥也没做,直接调用数据访问层那一套),已经比较好了,言下之意颇为自得的样子。我就先用NDepend分析了一下他们新的模块代码,告诉他们圈复杂度有点高,类有点大,同时在那周的Weekly Link里,我着重分享了OO里的几个设计原则,“单一职责原则”、“依赖倒置原则”之类的,又提到了单元测试的几个原则,比如不能连接数据库,而他们的代码要想测试,没有办法不连数据库。这些镜子照出来的瑕疵,为我的下一步计划提供了很好的切入点。那就是“单元测试”。

九、从一开始就要高标准


我在后来读到过一篇文章,讲的是大家总结下来的敏捷转型的十大建议,其中一个建议就是“从一开始就要高标准并且一直保持”,所谓高标准并不是要求团队一口吃成胖子,一下就达到高标准的要求,而是对团队一开始就设置高期望值,让团队拥有持续改进的动力和一个愿景。无独有偶,我前一阵有一天在公司翻到一本关于英语教学的书,里面第一章的标题就是“Setting High AcademicExpectations”,第一句话是“high expectations are themost reliable driver of high student achievement(高期望值是带给学生高成就的最可靠的驱动力)”,教学如此,敏捷也是如此。因此我想推单元测试,我一下就给他们来个高标准,就是TDD。

十、TDD的争议


TDD在网上是有争议的,有人视之为一个团队的必须实践,也有人认为它太浪费时间。我把它当作一个高级程序员和初级程序员的分水岭。我到现在仍然认为:至少在企业应用软件行业(由于我本人毕业至今做的都是企业应用软件,从没涉猎其他领域,所以对其他领域不妄言),一个没有掌握TDD的程序员,就是初级程序员,不论他技术如何。也许有人不以为然,认为有不少技术很好的程序员也不写单元测试,更不会TDD。可是仔细想想:这种人所谓技术很好,是他写的代码很有可读性?还是他只是对某些框架、工具或控件非常熟悉。为什么我提到“可读性”?因为一个企业应用软件,它的维护成本往往是开发成本的十几倍乃至数十倍以上(只有那些可读性非常好的软件,这种比例能在十倍以内),再考虑到我们每天在现有的代码库上写代码,都要先读读原来的代码。也就是说,一个团队花在读代码上的时间往往是写代码的时间的百倍以上,一点不夸张。所以一个好的程序员,写出可读性、维护性好的代码,比解决一个技术难题重要多了,而且说实话,在企业应用软件开发中,真没有什么很难搞定的技术问题,最难的是复杂无比又千变万化的业务。为什么我在说TDD的时候提到“可读性”?网上有些反对TDD的人把TDD当做测试来说事儿,真是令人失望。这些人不知道TDD首先不是关于“测试”的,它是关于“设计”的。这句话我经常在同事面前说,但同时我也知道,一个没有尝试或掌握TDD的人,不会理解这句话。

十一、“道”与“术”


有两种技术牛人,一种对各种工具掌握得很透,这是“术”上的高人,另一种对“道”理解得很透,面向对象设计能力、TDD能力。也许有的程序员会问“设计模式”呢?是属于“道”的范畴还是“术”的范畴?这个问题也是当初我在给南京团队做的一次技术分享中抛给他们的问题。他们的回答是设计模式属于“道”的范畴。我去别的公司面试架构师之类比较高级的职位的时候,面试官基本都会问问设计模式的问题,而我面试别人的时候,让对方说说设计能力的时候,对方往往也是拿几个常见的设计模式来说。似乎一谈到“设计模式”,就有了比较高级的感觉。但我的回答是,当一个程序员面对一个问题时,心里想着这里可以用“工厂模式”,那里可以是用个“观察者模式”,这时它属于“术”的范畴;而当一个程序员不断重构他的代码,不知不觉中重构出某种模式,或者重构掉某种模式,这才属于“道”的范畴。再说一件跟“道”与“术”相关的事情,一个前同事,前不久在微博上感慨:当初做ASP.NET WebForm时,花了无数的时间把WebForm这个框架研究得底朝天,可是那些知识现在一点都没用了,那些年被微软坑了。好的程序员“道”“术”双修,并且需要对“术”有辨别能力,哪些该学,哪些适可而止,有时确实很难分辨,靠的只能是经验。

十二、程序员文化


上面说到的这些看似与敏捷毫不相干,但我愿意把这些想法和南京团队交流。程序员就该有程序员的话题,如果一个团队的程序员之间只聊与工作直接相关的话题,那我觉得这是一个没有文化、不注重个体的开发团队。敏捷首先是一种文化。而上面这些话题,其实就是对我们日复一日代码生活的一种反思,对我们程序员职业的一种反思。一个连对自己职业都不反思的团队,又谈何敏捷呢?敏捷的一个机制就是反思过去,从过去的事情得到反馈,从而得出下一步的行动。

十三、程序员与建筑工人


所以我花很多时间跟他们聊这些反思性的问题,有一次谈到“程序员和建筑工人”的类比。如果把“开发软件”和“盖房子”类比(当然不是平房,是国家大剧院之类的大型建筑),那么“建筑工人”就对应“程序员”,“建筑设计师”就对应“架构师”,对吗?这是很自然的对应关系,但我告诉南京的同事们:错了。程序员就是设计师,程序员写出的代码就是设计师的设计图纸,建筑工人按照设计图纸施工,建造出高楼大厦,那么,谁把程序员的代码生成软件?编译器。建筑工对应的是编译器,不是程序员——这观点不新鲜了,国外几十年前就有了,可是国内的程序员知道的还真不多。有了这样的类比,再来思考软件行业和建筑行业的区别——建筑是软件的一个很常见的隐喻。一个最大的区别就是:设计图纸一旦交给建筑工人开始施工,再做修改的可能性就很小了(总不能把施工了一半的建筑推倒重来,成本太大)。而我们程序员就不一样了,我们写出代码,交给“编译器”编译成软件后,仍然可以无数次地修改代码,重新编译成软件。“从代码到软件”,这里的成本几乎为零,跟“从设计图纸到高楼大厦”截然相反。这是我们程序员幸福的地方,也是应该加以利用的地方。这就是我们为什么可以甚至应该“重构”、“TDD”——建筑设计师没办法写出一部分设计图纸后,交给建筑工人去施工验证,然后根据验证结果的反馈来重构并改善他的设计图纸,但是我们程序员可以。

十四、TDD不是目的,“拉”与“推”

事实上敏捷本身也不是目的,写代码本身都不是目的,解决用户的问题才是目的。一切问题都是用户的问题衍生出来的。如果没有用户需求,就不需要软件的各个特性,如果不需要软件,就不需要开发软件,就不会出现开发中的各种问题,就不需要敏捷转型来改善那些问题……而在一个使用Scrum已将近3年的团队,作为技术主管,虽然使用TDD作为一个深化敏捷的突破口,但我也没把TDD本身当作目的。我心里其实根本没有对团队掌握TDD抱有幻想,我只是想通过TDD来拉动一些东西(敏捷就是要建立各种居于“拉”的模型,来代替传统的居于“推”的模型)。要做TDD,就得有面向对象设计和编码能力,就要尽量遵循SOLID原则,而TDD又要从最重要的Domain层入手,那就要求Domain层对数据访问层有解耦,这就拉动对架构的重构(原有架构没有这种解耦)。有了单元测试,就希望它经常被执行,就会拉动持续集成的需求,要做持续集成,就有把三个Scrum团队的代码合在一起的需求。而这些都是针对上面“更多挑战”中提到的各种问题。这就是我的思路。

十五、Coding Dojo


尽管构思要大,对团队期望值要高,但路还是要一步步走。现在回想起来,那一年里给我留下最深印象的就是Coding Dojo(代码道场),由于我在上海办公室上班,只好采取计算机远程连线的方式。我们从代码库里挑出比较有重构价值的一段代码,然后大家一起来重构。刚开始的几次一般是我来做,他们看着。我每做一点重构,都会把背后的原理、为什么要这么重构说出来。对每一点都要说出为什么,有时候也不是轻松的事情。有些对我来说显而易见的东西,他们也会来挑战,提出其他想法来。这种交互是对团队所有人的一个思维碰撞,很有意义。后面则是大家轮流来重构了,每次上来两个人坐到计算机前,以结对的方式来重构,其他人观摩,一段时间后可以有问答和交流时间。

十六、让实践落地

做完一次Coding Dojo后,我会把过程中记录的那些“为什么”做成思维导图,发给大家。让大家有回想和复习一下的机会。为什么?因为敏捷中有很多实践,不管哪些实践,最忌讳的就是没有跟踪。比如有些团队,每个Sprint结束后照例都有回顾会议,会议上总结出不少Action,可是会后不再跟踪,只要发生一次这样的事情,整个团队的“心气”就下去了,“心气”这东西下去容易,上来就难了。Loding Dojo同样如此,我们在道场上提及的那些写好代码的原则、重构的原则、面向对象的原则,如果只发生在LodingDojo,回到现实写代码时都忘了或不再努力遵守,那这种Loding Dojo就不再有意义,让团队成员感觉到“现实”和“理想”的反差——这是敏捷继续深入的一大障碍,因为他们会觉得敏捷那些东西“不现实”。一旦有这种想法,团队成员的“热情”就会慢慢消退。敏捷是关于人的,最重要的就是人的热情。

十七、程序员的产出


我后来离开C公司去别家面试时,说起这段经历,面试官往往会问:你们有那么多时间做这些吗?我想有些面试官的潜台词是:什么Coding Dojo,不是不务正业吗?进度怎么办?要完成的用户故事怎么办?一个程序员的产出,是很难被衡量的。如果他干得高兴,他也许会在一天中花3~5小时以很高的效率来做事,如果他干得不高兴,他也许一天6~8小时都在勤勉地干活,可是做出来的东西并没有多少价值,或者很多Bug。我见过很多这样的例子,我相信很多人对这个例子也不会陌生:一个程序员花了8小时完成一个故事,可是后面由别的程序员(或者他自己)又花了超过8小时来修复Bug。很常见吧?那么这个程序员开始花的完成故事的那8小时产出就是负的。如果时光回到那8小时,这位程序员能对团队、进度做出的最大的贡献就是什么也不要做。

十八、权威


我现在回想起来,在我和南京团队做过几次CodingDojo后,我在他们心目中的权威已经多多少少树立起来了。现在来反思一下两个问题:一是需要有什么样的权威?二是怎么树立这种权威?关于第一个问题,权威有好几种来源,一种来自权利,主管和领导天生就有权威,他如果想要团队做什么,团队基本上只能照做。另一种是“专家式权威”,有句话说:“一个团队的能力是由团队中能力最弱的那个人决定的。”这叫“木桶理论”,而我的一个体会是反过来:一个团队的潜力是由团队中能力最强的那个人决定的,也就是说,这个团队潜在能达到的高度,就是能力最强那个人的高度。团队中专家的作用就在于拔高这个潜在的高度。一个团队能力低是暂时的问题,可是一个团队潜力低,那才是真正的问题。可是专家是怎么来的,我作为一个技术主管,很有可能我自认为是专家,可是在团队成员眼里是“砖家”,如果是这样,“砖家式权威”只会带来别人的不服。这就产生了第三种:“认同式权威”,别人认同你。这个不一定需要你是专家,很可能别人知道你拿出的方案不是最佳方案,可是还是认同你,愿意去尝试。

十九、认同权的建立:无私,勇于说不知道


我并没有专门研究过这些,我只说说我从实践中感受到的几点。我是怎么得到团队认同的?首先是无私分享。在Weekly Link中,在Coding Dojo中我都是把自己所有的老底都抖出来了,完全没有藏私的念头。现实中怕别人超过自己因而藏私的现象其实是很多的,其实那样无助于自己的提高。其次是谦虚、勇于说不知道。谦虚这种传统美德,不能被忽略。我以前工作过的公司有个技术总监,技术确实非常了得,几乎无所不能。我们碰到技术问题去问他的时候,基本都能给我们满意的解答,但是如果碰到他暂时回答不了的情况,他会说“你这是一个好问题。”在我印象中,他没有说过“这个我也不懂”之类的话。想想,你不说“我不懂”,而是各种掩饰,那些提问的人会看不出来吗?他会知道你爱面子,于是他下次想问你问题的时候,如果琢磨着你可能不懂,他就不来问你了!你爱面子,他就彻底成全你。反过来,你如果坦率地说:这个我也没研究过,不太懂,不如你花点时间研究一下,有结果后也教教我。这样的结果是什么?一是带给对方动力,他觉得他要研究这个东西反过来教会这位专家,动力一下就来了。二是拉近双方距离,他不再觉得这位专家是高高在上的人,会产生亲切感,下次有问题,他仍旧很愿意来问。这种人与人之间良好的互动,也许很细微,可是仍然在某种程度上影响着这个团队在敏捷道路上的步伐。敏捷团队需要权威和领导(Lead),但不需要在各个方面都在行的权威,而是需要每个人在不同方面都能担任权威和领导(Lead)。

二十、成就感:点燃程序员的热情


当南京团队在日常工作中开始主动积极地尝试“TDD”,当我发现我在Coding Dojo中强调的那些概念和术语(“单一职责原则”,“充血模型”等)在他们日常工作中经常被提及并互相提醒时,我知道他们的热情被点燃了。反思一下,他们的热情是怎么被点燃的?我想最根本的一点是:成就感。很多软件团队,每天赶着进度,进行着枯燥的编码生活,其实并得不到多少成就感。想想一个无聊的周末,你是愿意读一本技术书或者英文书呢?还是玩一把自己喜欢的游戏?大部分人都会选择后者,为什么?就是因为后者能很快带来一种成就感,比如玩CS,我在几分钟内就能体会到我枪法准、爆头带来的成就感,这种成就感会吸引我继续玩下去。而读技术书或者学英文呢?也许我要坚持半年才能看到自己的提高,成就感要在半年之后才会来临,这就是很多人坚持不了的原因。程序员同样如此,日复一日地写些没什么意思的代码,写完又无休止地改Bug,成就感何在?激情自然也不会有。那现在能从日常工作中不断互相学习如何写好代码,每天都能看到自己的进步,我相信这对大多数程序员都是很有吸引力的。很少有程序员是不求上进的。工资是一年才调一次,软件是半年才发布一次,都很遥远,而每天都觉得自己技术上有进步,这种快速的反馈和成就感,我想是对激发热情最大的帮助。

二十一、PDD:痛苦驱动开发


团队有了热情,我们绝对不能辜负。我决心帮助解决他们最痛苦的地方:VSS。它慢,它让南京团队一周看不到新代码,它让南京团队提交的代码一周后才会被集成进去、才能知道有没有问题,它的排他式检查让团队成员没法同时工作在一个文件上,因而有时他们明明知道需要一个公共函数,可是也各写各的。代码文件在VSS中的存放结果也是混乱的,必须把某文件获取到本地的A文件夹,然后把另外一些文件获取到本地的A文件夹下面的B文件夹……本地才能编译。这想想都痛苦。我把它迁移到了SVN,我重新整合了solution文件,让三个Scrum团队都工作在同一个solution文件下,我构建了本地编译脚本,只需双击就能构建整个项目并执行所有的单元测试。我调整了文件结构,只需把SVN上根目录获取到本地任何目录,本地即可编译。南京团队连接SVN也相当快速,于是我进一步实现了“持续集成”。这个花了我大量的时间,可是解决了团队的“最痛点”,说白了就是“持续集成”这个敏捷实践而已。关键是任何实践,都要做透,我见过很多人说起“持续集成”,以为就是每次签入后自动编译或部署而已,这都是只知道点皮毛的。很多敏捷实践如果只做皮毛功夫,那会让程序员觉得是来压迫他们的(比如估算、Sprint),而做透了才知道他们是来帮助程序员的。

二十二、排除障碍,创建舒适的技术环境


也许我上面说的“最痛点”是个特例,很多团队没有这个问题,可是有时候面对团队太多问题的时候,找到最痛点去解决它,然后寻找下一个最痛点……这也是很可行的方法。另外,我想强调的并不是那些痛苦本身,这也是为什么我只花了小篇幅来介绍它(尽管花了我大量的时间去做,光说服主管,让他知道从VSS切换到SVN并没有多大风险就花了九牛二虎之力,还要对团队成员进行SVN的培训、非排他式检查的融入(merge)问题、CI的过程、签入代码的节奏和周期等最佳实践)。我想强调的是,做这些事的本质在于排除障碍,排除任何让程序员不爽的东西。之前光获取代码就费老大劲,现在飞快,随时可做。我甚至定制了一个脚本,名字叫“goodmorning.bat”,每天早上程序员开始上班时,双击它,就会自动把最新代码获取下来并编译,打开我的工作目录……也就是说,早上来到公司打开电脑,双击“good morning.bat”后,一切就绪,就可以开工了。每写完一段代码想签入时,我不需要用IDE去编译我的代码,因为那会比较慢(事实上专业程序员不应该用IDE来构建或部署,IDE是用来开发时使用的),我只需双击本地脚本,就在几秒到几十秒中编译并执行单元测试,告诉我一切正常,立刻得到信心进入下一步工作,跟之前签入之后如果不能编译通过,都需要好几天才知道的情况,截然不同了。总之一切都变爽了。Scrum里面,ScrumMaster的一个很大职责就是要排除障碍,作为技术主管也一样,也是要为程序员排除日常工作中的一切不爽的东西。这样工作才能“流动”起来。

二十三、投资回报率

程序员追求好代码、追求单元测试的热情被激发,持续集成也已设置好,相当于燃料已经准备好,下一步就是开足马力往前开了。追求单元测试的覆盖率是很不现实的,而且我到现在认为也是没有什么意义的。就算“代码覆盖率”为100%,“逻辑覆盖率”也达不到100%,就算“逻辑覆盖率”很高,可是就一定能提供价值吗?一切都要考虑投资回报率。单元测试的代码对用户来说并不直接提供价值,因此它需要更干净,更可读。每个单元测试都应该有文档的作用,应该封装了一小片业务逻辑或者设计逻辑。另外单元测试是为了驱动出更好的设计。这两个才是主要的作用,测试是排名最后的作用。所以我们不考虑覆盖率,我们从最重要的地方入手。软件的设计一般都分三层:UI层、业务逻辑层(BLL)和数据访问层(DAL)。哪个是最重要的一层?显然是BLL。我们的单元测试就从这里开始

二十四、让领域模型裸奔


业务逻辑层封装着现实中客户的业务需求、他们的日常业务规则,我们开发软件的目的就是解决他们的这些需求。因此“业务逻辑层”就是我们要解决的“问题”。UI是可以商量的,存储逻辑也是可以换掉的,这些都是解决方案的领域。用户不在乎你的“解决方案”,用户在乎他们的“问题”。我面试高级程序员的时候一定会问一个问题:BLL与DAL的依赖关系是怎样的?大部分人会说BLL依赖于DAL。其实没什么错。我接着问:BLL是应用程序的核心,为什么要依赖于数据存储技术?有没有办法解耦?让数据存储层服务于我们的BLL?很多人来面试高级程序员却回答不上来。其实答案很简单,就是依赖翻转(IOC),对DAL层抽出接口(IDAL),BLL依赖于IDAL,BLL和IDAL在一个包里,而DAL实现IDAL,所以DAL依赖于BLL那个包,只不过在运行时(Runtime)时把DAL注入。BLL对DAL的依赖从编译期延迟到了运行期。如此BLL在编译器已不依赖于任何东西,除了IDAL,而在对BLL的单元测试时,使用Mock来替换IDAL。于是我们真正地第一次拥有了单元测试(不连接数据库的测试)。团队成员对单元测试的需求,驱动出IOC和DI的区别和联系,依赖反转原则等这些概念。他们对这些概念就有了更深的理解。这也是一个技巧,一切敏捷实践,要说出为什么。

二十五、架构


为什么很多高级程序员也说不清上面说的那些概念?我想一个原因是,在很多公司,这些都是属于“架构”的范畴,是属于“架构师”需要思考的,一般的程序员不会去考虑这么高级的问题。那么,到底什么是架构?我们程序员在设计一个类的时候,是不是在做架构?不是。那么架构师在设计大粒度的东西时,就是在做“架构”,为什么?可不可以说架构就是一些“大”的设计。Martin Bob大叔有篇博客说到这个问题,他说:“架构无非是设计中不可逆的部分。”软件开发无时无刻不在设计,一切都是设计。但有些设计是可逆性强的,比如一个类,如果设计错了,改动的成本小;而有些设计的可逆性很弱,比如选择了Entity Framework作为ORM层,再重新换别的ORM框架甚至不想用ORM层了,这种改动的成本就非常大了。

二十六、你做什么就是什么(You are whatyou do)


MartinFowler很久以前的一篇文章因此说到:有两种架构师,一种就是传统的架构师,他非常厉害地做了很多“大”的设计(也就是架构),他的设计可逆性差,但尽量合理,因此需要“可逆”的可能性很低。另一种架构师尽量增强可逆性,哪怕是很大的设计。一种架构师建立架构,一种架构师通过增强设计的可逆性来消除架构。或者说这是“架构师”的两顶帽子(“建立架构帽”和“消除架构帽”),好的架构师在面对不同领域时会选择带上不同的帽子。而“消除架构帽”是所有程序员都可以带上的——这是我对南京团队的期望。Scrum中只有三种角色:PO、SM和Team。这个团队是个跨职能团队,有厉害的架构师固然是好事,但每个人都应该有架构师的视角,同样每个人都有QA的视角,前几天看到一句话:不给成员打上标签,你做什么就是什么(don’t labelyourself, you are what you do)。这句话其实就是我当初想对南京团队说的,也是我想对每一个敏捷团队说的。


二十七、Scrum是不行的,如果只有Scrum


现在回想,为什么我作为一个技术主管的身份去推动一个团队的敏捷朝更深入的地方发展,会取得还不错的效果?我想其中一个原因是我很注重技术实践。Scrum是个简单易行的管理框架,大部分敏捷转型团队都会使用Scrum作为起步。但Scrum是根本不够的,它不涉及任何工程实践,而软件团队最终是要用代码来说话的。Scrum用得再好,代码出不来,技术债很多,最终团队会被拖累,然后Scrum就成了表面文章——这样的例子我见得很多。或者说,“伪敏捷”的形成,除了管理层的因素外(管理层经常是最大的Impediment,没有之一),另一个重大因素就是团队工程实践的缺失。反过来想:如果一个团队要用Scrum,它必须每个迭代都有一个发布,而每个发布必须经过回归测试,那么想想:几个迭代之后,你是不是需要100个QA来做回归测试?因此你必须要把测试自动化。而自动化测试很难,必须有配套的很多东西,必须有不断的培训……也就是说,真正的Scrum很难。知难而退的团队有多少,伪敏捷就有多少。

二十八、做正确的事情vs正确地做事情


如果把敏捷比喻成一场大戏,我前面做的工作可以说是设置好技术上的布景。持续集成、单元测试、重构、面向对象原则……但是,仅仅有技术实践也是不行的,我们最终得唱好戏才成。单元测试只保证正确地做事情,如何保证“做正确的事情”?有一天发生这样一件事:几个程序员就一个UI的实现问题激烈讨论,得出的结论是很难做,至少需要一周。最后找我来确认,问我是不是还有更好的技术实现方式?我听完问他们:“你们在解决什么问题?”他们:“我们在解决这个UI的实现,挺难的……”我:“我是问你们在做哪个用户故事?”他们告诉我说是StoryA。我问:“这个Story是要解决用户的什么问题?用户的日常业务里有什么逻辑在里面?”他们回答不上来。然后我就找来PO,跟他们一起说清楚这个用户故事背后的业务逻辑,背后的“用户”的问题。弄清楚以后,我就问他们:“为什么不用另一种UI设计,简单得多,同样可以解决这个用户的问题,然而只要2小时。”如果他们没有在做“正确的事情”,那么他们越是“正确地做事”(TDD、CI、Pair等),他们就错得越多,因为他们浪费得就越多。

二十九、问题和解决方案,5-whys


很多软件开发团队都非常容易犯这个错误:他们在激烈讨论某一个技术问题,讨论如何实现,却不知道背后的用户的业务逻辑。他们在解决他们自己的技术问题,而这些“技术问题”对于用户来说只是种“方案”,而软件团队要解决的是“用户的问题”。问题和解决方案是一对多的。在不用Scrum的时候,“需求规格书”是很常见的一种东西,可是有多少“需求规格书”里把UI画上去了?UI不是需求,UI是一种设计。当把这种“需求规格书”扔给团队去做的时候,相当于把某种“解决方案”直接扔给了团队。而团队就开始从代码上解决这些“解决方案”。于是,团队花九牛二虎之力做出来的东西,很可能根本就没解决用户的问题。因为有用户的“问题”,所以我们有软件“方案”,而一旦选定一个“方案”,这个方案就成为软件团队要解决的“问题”……如此反复。不要沉溺于后面的问题,那往往是技术问题,往往是“我们的”问题,不是“用户的”问题。要时时记得用户的问题。反过来说,我们解决一个技术问题时,要记得问5个为什么,直到问出“用户的问题”。(为什么要解决这个UI的实现问题?因为我们选定了方案2.1;为什么选定方案2.1?因为PO选定了方案2;为什么要用方案2?为了解决用户的“问题1”。)确定问题,而不是解决方案。

三十、“为什么”


实际上,Scrum中的用户故事(User Story)背后就隐藏着这样的智慧。我们都知道有这样的格式:As a (who), I want(what), in order to(why)。这个最后的“why”,就是用了描述“用户的问题”,或者说商业需求。然而,在我们的团队里,虽然当时使用Scrum已经三年之久,但所有人都对用户故事这背后的智慧一无所知。所以PO也觉得用那样的格式来写用户故事没什么意义。于是他们会使用他们的方式来写用户故事。类似的事情不少,比如,他们觉得在计划会议上大家一起估算也没什么意义,大家一起拆任务太浪费时间。于是他们只让一个人去做这些事情,一个人完成估算,拆好任务,贴在Scrum板上,完毕。在我跟他们解释了“估算”和“拆任务”背后的“为什么”后,他们答应回到大家一起来做的方式。敏捷团队就是不断问“为什么”的团队,好的ScrumMaster/敏捷教练就是不断引导团队“问出为什么”。

三十一、估算(动词)很有帮助,但估算(名词)往往没有

以估算为例,为什么团队估算(动词)很有帮助?因为它能揭示大家的理解和所拥有知识的不一致,因而尽早地打破这种鸿沟;它会把团队成员心里的假想(Assumption)暴露出来;通过暴露假想,团队能发掘出更多的细节和问题。有不少团队会这样:团队估算出一个用户故事的大小,管理者把它换算成时间,然后告诉团队说:这时间太长了。“估算(名词)”表示的就是估算的结果,团队不应该被估算的结果逼迫着时程,因为那会打破信任,从而让下一次估算成为形式和欺骗。估算是最典型的“重在过程”的敏捷实践;如果不知道为什么,它就会被团队抛弃。

三十二、守破离


当然,敏捷团队不是一开始都能回答出很多东西的“为什么”。这就是为什么要遵循“守破离”。很多“为什么”是要经过大量实践才能回答出来的,刚开始实践的时候并不知道为什么要这样,但是不能因为不知道就不做,这时候要“守”——继续按照书本上说的去做,直到悟出为什么,知道了为什么,也许团队可以根据自己的情况调整实践做法。这就是可以有一些“破”了。守破离跟时间没关系,比如我们那时实践了3年Scrum,在还需要是“守”的阶段,却以为自己到了“破”的阶段,于是敏捷就流于表面了。守破离的理论是一种模型,不要让团队只知道去敏捷转型,而且让团队更多地知道一些帮助敏捷转型的“模型”,往往有事半功倍的效果。

三十三、测试优先


记得那时整个团队花了一段时间着重加强了一下用户故事的书写,我们更多地去讨论用户的业务场景,更多地考虑用户的问题而不是我们自己的问题。然后团队开始寻找下一个痛点:小Bug不少,用户故事已经足够小,但是仍旧会拖比较长的时间才能完全达到完成(Definition of Done)的要求。当时的工作流程是这样:进入Sprint之后,开发者开始写代码,QA开始写测试用例;开发者完成后给QA测试,QA发现Bug再交还开发者去修改。在我跟团队介绍过Lean的一些原则后,他们知道这里存在很多的浪费,也导致开发者的泳道经常打破WIP的限制。我们改善的方法很简单:QA每写完一个用户故事的测试用例,花10来分钟跟开发者检查一下,花的时间不多,但是效果很好,因为在检查的时候,也是一个开发者和QA统一认识、互相提醒的过程。这时候往往开发者还没开始编写代码,却可以通过测试用例的检查,捕捉到很多边界条件的验证。代码未写,测试用例先行,这是最简单的测试优先。

三十四、QA vs QC


我进一步在博客上跟团队分享了QA与QC的分别:QC是测试并找到Bug;QA则是预防Bug。QA不应该在用户故事生命周期的后半段才介入,而应该从一开始就介入。遗憾的是很多公司的QA都干着“找Bug”(而不是“预防Bug”)的活。
三十五、分享:Wiki、博客、书籍、技术讨论、编程练习
事实上,不仅我自己会写博客,还有好几个同事都在我的鼓励下开通了博客,写文章不一定是要教会别人什么,更重要的是寻求一种反馈。我们还搭建了一个团队Wiki,把一些知识点顺手都记录下来。因为敏捷团队的一个特点就是文档变少了,测试用例也很简略,有些知识点只存在于某些人的脑子里。我们把他们记录下来,也是为了知识的传播。我们每两周都有技术讨论(Tech Talk),话题由大家自己确定,放在Wiki上大家投票,每次选取得票最高的作为下一次的主题。整个过程完全自主,没有管理层的介入。南京团队由于年轻有激情,我们还做了好几次的课外编程练习,每次出一道题,大家在业余时间独自完成,然后找个时间互相检查、评比,选出最佳作品,发个奖品以资鼓励。那时候分享的氛围空前浓厚。有了这种氛围,团队已经在正确的轨道上了,是不是敏捷其实真的都无须担心了。

三十六、没有银弹,只有持续改进


毕竟敏捷是一种价值观,是一种文化,它本身没有终点,甚至没有定义——没办法说这样是敏捷,或者那样不是敏捷。敏捷也不是银弹,敏捷不试图解决所有问题,敏捷只是可以用来解决一个个的痛点。如果一个团队不再持续改进,不管它用的是敏捷还是瀑布,终将失败;而如果它总在不断地发现新的问题,不断地改进,它已经在敏捷的路上了。所以,如果有人问:你们公司敏捷了吗?这是错误的问题。敏捷不是一种状态,是一种持续改进的姿态。

三十七、敏捷宣言


敏捷宣言的第一句话是什么?个体与交互高于流程和工具?不是,第一句话是:我们一直在实践中探寻更好的软件开发方法,身体力行地同时也帮助他人。

原创文章,作者:wbq521,如若转载,请注明出处:https://www.wangbq.cn/87.html

发表评论

邮箱地址不会被公开。 必填项已用*标注

联系我们

18706728980

QR code