飞机飞得再高,毕竟要落地。你准备好了吗?
实效的敏捷,第四届敏捷大会热闹非凡,上千元的票价抵不了庞大的支出,更阻挡不了人们的好奇和求知的欲望。说实话,我没有听到新的东西,不管是美丽的故事还是悲惨的故事,还是困惑的故事,我听得太多了。但是却并不阻碍我的兴趣,听这些故事,我没有打算找到答案。虽然我也问了很多问题,也被问了很多问题。但是对于绝大部分问题,我知道那没有答案。虽然我做出一副挣扎的样子,其实我的内心早就屈服了。
Brooks不是早就说了吗?——没有银弹。
比如,如何衡量绩效?敏捷了就很难衡量绩效了。很多人这样想,可是“Comparing With What”(Kent Beck)跟什么对比呢。当然是CMMx!那CMMx是怎么衡量的呢?代码行数?靠谱吗?测试覆盖率?圈复杂度?敏捷了就不能用了吗?这个世界上真的有衡量开发人员绩效的好办法吗?有吗?CMMx的时候容易吗?问一问自己的内心,如果你是一名经理,你是不是现在内心打个分数然后再去计算每个人的分数呢?如果你是一个开发人员,你真的认为代码行数能反映一个人的水平吗?还是你的主观判断更靠谱?如果你是一个测试人员,你是否厌倦了以发现Bug的数量来衡量绩效的办法(顺便表达一下对使用这种绩效评估的领导的郑重的鄙视)。今年的D应该轮在谁的头上,还是那个新来的吗?别跟我说,你当新兵的时候没"被"得过D。如果你的工作就是绩效评估,你的内心是否挣扎过,到底应该以什么样的指标来衡量?你是否在内心确信你定义的指标足够“公平”合理?你是否确信你看到的数据不是“做”出来的?顺便诸位项目经理哪位没有“做”过数据?为了测试覆盖率跑跑那些从来不用的测试?为了追赶发布期,警告测试部门尽量手下留情合并缺陷?为了圈复杂度拆分函数而没有同时分析提取函数是否恰当?难道是我“恰好”碰到了这些极端的例子?怎么办?敏捷来了,难道还没有办法吗?我不知道。你要问问自己,我到底要干什么?我为什么去衡量?我能不能做得更符合我的目标一些?仅此而已,你永远做不到公平,在软件行业尤其如此。总的来说,软件行业还是工匠式的行业,开发还是一门手艺。你要任何一个容易指标都很容易导致“局部优化”,任何一个真正有价值的指标都不那么容易量化。在敏捷开发中常见的量化衡量团队开发能力的指标是Velocity,但是这个看上去量化的指标只能用来衡量一个团队自身的进步,不具有任何横向对比的意义。要去提高Velocity,最简单的办法就是将每个故事的点数乘以2。你要换算成人日数吗?你会得到非常有趣的比较,那就是A团队平均每个人每周完成的工作量是5个人日,这个碰巧跟B团队的平均值相等。(这跟某次世界杯上,总进球数和总失球数恰好相等非常类似。)
所以,我们的建议是,这些指标只能被度量,不能被管理,而且这种度量是以反馈为目的的,而绝不是以绩效为目的的。在敏捷团队中我们有很多指标,很多衡量手段。比如签入代码行数、测试覆盖率、圈复杂度,我们用这些指标来帮助我们分析团队的状况。比如,测试覆盖率下降了,我们要看看,为什么下降了,是不是单元测试无法覆盖某些场景,我们是不是需要增强集成测试和系统测试来保障系统的质量;再比如,圈复杂度提高了,我们会去考虑是不是最近重构做得不够频繁了,需不需要专门留出时间来做一些重构。当这些指标成了反馈手段,我们不需要“做”任何数据,CI的红灯和绿灯都是我们的朋友。(这些想法都属于“基于信任”的管理方式,关于对于这种方式的质疑与回应参考光磊的文章)。
====随便分个段,因为跳跃性太大==
下午的TDD情景坊来了一个做SQA(记不清了)的同学,指出我们是在误人子弟。我们的方法命名不符合Java的某个“规范”,测试命名不符合工具“要求”,没有注释。总之——一塌糊涂。我能看出他的好意,但实在没有给别人解释的机会,就开始演示他的TDD了。我试图跟他解释我们为何方法这样命名、为何测试类不能对应被测的类、为何我们“基本上是反对注释”的。但结局是“完败”。
要实践敏捷,开放的心态是非常重要的。今天正好在看Git的源代码,发现Linus竟然是在用“Ruby的命名规范”来命名C的函数(如int diff_tree_stdin(char *line))。我很后悔没能把Linus抓过来跟那个同学过过招:)
下午跟某公司的同学聊到测试分层的问题,另一个同学插话说他们的测试很难定义边界值。我跟他说这个问题和测试分层是正交的。也就是说,无论采用什么样的分层方式,问题总是存在的。这里说正交其实并不严格。我们可能会通过某种测试分层方案来缓解这样的问题。比如,边界值也许会在较低层的测试上容易确定。但是,从根本上说,原来的问题还是存在。但是,这个问题是不是就是一个测试问题,却又是另一个话题。也许是一个设计问题——Kent Beck响应式设计。
软件开发中有很多问题并不是依靠一个实践就能解决的,而且还有一些问题并非现有的每个实践能解决的。这个时候追踪背后的问题是非常重要的。我见过一些团队,单独一个团队不能交付任何特性,甚至自己的代码编译都不能通过,必须与其他团队协作才能编译、验证。遇到这样的问题怎么办呢?
这可能是一个组织问题,也可能是一个设计问题,但是我们见到很多团队选择从哪些角度去解决它呢?配置管理,测试框架,流程管理。我一一介绍一下:
配置管理:既然你必须跟别人在一起才能编译,不然把你们放到一个分支上,互相能访问对方的代码。
测试框架:既然你自己不能编译,我能不将你的依赖Mock掉或者Stub掉?这样你就可以针对某个Mock框架或者一些Stub代码进行编译验证了。
流程管理:我们也可以规定A团队必须在几点几分提交,然后你再编译。
这些方法好不好?作为一个“响应式改进”(向“响应式设计”致敬)的倡导者,我不会说好还是不好。我只想让你问问自己:这种选择是不是符合你内心的“真北原则”(参考《成功人士的7个习惯》)。你有没有分析从设计或者组织的角度去做改进的成本和收益?你的选择是不是考虑和长期收益和短期收益的平衡?如果你全面的分析了各个原因之后,仍然选择用上面的这些“间接方法”去做改良,那你的选择就没有问题。
温伯格说:不要把解决问题的方案当作要解决的问题本身。
即使我们选择了上面的方法去解决我们的问题,我们在内心要始终记着我们要解决的是什么问题,并且在任何下一次再采用同样的方法去解决这个问题的时候,要问一下自己“现在的背景是不是跟我们上一次做决定的时候是一样的”。再做一次符合内心“真北原则”的选择,而不是直接就选择先前的那个方案。
====再分割一下====
昨天中午有幸跟Kent Beck一起吃中午饭,我问他响应式设计是不是从XP风格设计的一种回归。或者说响应式设计是不是介于传统设计和极限编程风格的设计(比如TDD)之间的一种形式或者思想?(原话:Is responsive design a step back from XP style design? I mean on the road that we transform from traditional design to xp style design。Is responsive design somewhere between the two styles of design?)。
本来没有期望太惊人的答案,因为我确实觉得传统设计太“保守”极限编程太“激进”,而响应式设计正是某种形式的折中或者回归。但Kent Beck思考了一会儿却说,不是回退,而是更进一步。极限编程提倡把设计的决策推迟到实现特性的时刻,而响应式编程则更推迟了,你要判断上下文决定是否设计或者作出何种设计。(仅根据我的理解整理,不确认这是Kent Beck的本意。)
===想起Dave的Broken Window理论,明天继续写。