飞机飞得再高,毕竟要落地。你准备好了吗?
实效的敏捷,第四届敏捷大会热闹非凡,上千元的票价抵不了庞大的支出,更阻挡不了人们的好奇和求知的欲望。
说实话,我没有听到新的东西,不管是美丽的故事还是悲惨的故事,还是困惑的故事,我听得太多了。
但是却并不阻碍我的兴趣,听这些故事,我没有打算找到答案。
虽然我也问了很多问题,也被问了很多问题。但是对于绝大部分问题,我知道那没有答案。虽然我做出一副挣扎的样子,其实我的内心早就屈服了。
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理论。