别给我准备log配置

May 9th, 2013 3 comments

老婆爱干净,地板每隔一天都要拖一下,我虽没那么爱干净,不过在这样的环境中时间长了,看到肮脏的环境也会感到不愉快。回想起大学时期,宿舍里的各种垃圾,方便面桶、香蕉皮、零食口袋……堆一星期而没人清理,实在是脏乱不堪,不过当时大家也都不以为意。所以人的适应能力是很强的,否则也不会在地球上繁衍70多亿的数量。程序员可能普遍都是爱干净的,然而如果在一个脏乱的环境里待时间长了,对俯拾皆是的小垃圾就会视而不见。

这几日给一个模块写测试,为了能把测试写出来,调整了很多地方,最后测试跑起来了,心情好了不少,这略表不提。我想详细说的是一个几乎不是问题的问题。每次跑完测试,项目根目录下就冒出来一个log.log文件,是项目根目录,不是target/那样专门放生成文件的目录,然后svn st一下,就一个个的问号,烦不胜烦。第一反应自然是找找项目下哪里的log4j.properties乱搞了,于是手工看了几个常见的地方,未果;然后用find grep之类扫一遍项目目录,未果,擦!影响心情有木有?根目录下堂而皇之地冒出来一个文件,我却不知道它哪里来的,被羞辱的感觉有木有?

既然项目里没有,那自然是某个依赖里包了个log4j配置,而且这个配置还说把日志打到log.log中,尼玛,得揪出来。

第一步,把所有依赖解出来,放到一个统一的地方给爷检阅:
$ mvn dependency:unpack-dependencies -Dmdep.useSubDirectoryPerArtifact=true

第二步,扫一下目录,把log4j配置揪出来:
$ find . -type f -regex ".*log4j.*"

进一步的,可以看看配置中包含log.log没有:
$ find . -type f -regex ".*log4j.*" | xargs grep "log.log"

然后,不出所料,发现有两个依赖(不是一个)包含了log4j配置,其中一个生成了log.log,另一个更不靠谱,log文件指向了/home/abc/xxx那样的一般机器上根本不存在的地方去了,只是一直被前一个log配置覆盖,没暴露出来而已。

于是发信告知让这两个包的维护者把log配置干掉,因为发布给别人用的依赖包根本就不应该带有log配置。我自己跑测试的时候,跑应用的时候,日志怎么打是我自己关心的事情,我会自己写配置的。最要命的是,如果别人给我藏了个配置,我很莫名其妙!而且这还是个不合理的配置。

这个问题存在不是一天两天了,通常大家的做法是看到log.log就加一条svn:ignore,这等于看到地上有块香蕉皮就把步子迈大点跨过去,不扯到蛋就没事。然后香蕉皮越积越多,步子越跨越大,蛋总有一天会不保的。而要推行规范化的持续集成,这样的香蕉皮得每天不停地扫,做一次很简单,但要推动一个团队养成这样的习惯,就不是件容易的事情了。


原创文章,转载请注明出处, 本文地址: http://www.juvenxu.com/2013/05/09/dont-prepare-log-config-for-me/

实例化需求的威力

April 15th, 2013 No comments

2011年12月份上海的AgileTour大会上,Gojko Adzic 发表了一个题为 Take the business along for a ride 的演讲。演讲中的一个故事我至今印象深刻,故事的主角是F-16战斗机,它的最初的设计需求是飞行速度要达到2-2.5马赫,首席设计师 Hillaker 询问美国空军为何需要如此快的飞行速度,答复是“飞机必须能从战斗中逃脱”,然而,意外的是,Hillaker 的最终设计并没有达到2马赫,但通过无框气泡式坐舱盖、倾斜的座位、侧装式控制杆等等诸多创新技术,飞行员可以非常敏捷地从战斗中逃脱,并且这样的设计生产成本更低!看似明晰的“2.5马赫”并未有效地传达真正的需求,那只是一种解决方法,Hillaker 通过理解真正的需求而给出了更好的设计,这是成功产品设计的本质!

Gojko Adzic 是《实例化需求》的作者,该书在 Amazon.com 获得了超过了20条的几乎全五星的评价,而在今年的9月,本书又获得了软件开发图书领域最富盛名的 Jolt 大奖!这一奖项意味着《实例化需求》基本是上从2011年6月到2012年7月这一年中对软件行业影响最为重大的一本书。

这到底是一本什么样的书?它与我在本文看透提到的故事又有怎样的联系?在第一章 Gojko Adzic 就清晰地说到:

在过去的十年里,软件开发社区致力于使用“正确”的方式构建软件,关注使用技术实践和思想来确保质量。但是,正确地构建软件构建正确的产品是两码事,我们要二者兼顾才能取得成功。

这是一本讲述如何构建正确产品的书。

我们的行业太需要这样一本书了!往身旁看看,程序员醉心于技术而不太关心为什么要开发手头的产品特性;客户说不清楚自己到底想要什么;业务分析师筋疲力竭地试图理解客户真正的需求并传达给程序员;测试人员一遍又一遍地运行着手工测试。大家辛辛苦苦忙活好半年,出来的产品特性中有百分之多少有真正的业务价值的?又多少产品特性其实是无法给项目干系人带来收益的?再看一看当今流行的敏捷方法论,例如极限编程和 Scrum ,在如何构建正确的产品方面它们其实并没有太具体的论述。Scrum 是抽象的框架,定义的内容非常少,在如何与客户协作方面它只定义了几个角色的一些必要的会议;极限编程中有一条实践叫客户测试(也叫验收测试),但也定义得很简单,具体怎么做?会遇到怎样的困难?需要多大的成本?这些都是大家更为关心的内容。

怎样的产品才是正确的产品?客户知道吗?也许知道一点;产品分析师知道吗?也许知道更多一点;测试人员的知识挺重要;程序员当然也少不了,但无法完全依赖于他们。看来只有提炼各种角色人员的知识才能得到正确的产品,因此协作是必须的。当然大家都知道要协作,可说起来容易做起来难,怎样的协作方式效率更高?Gojko Adzic 说要借助实例的力量!他还进一步总结了一套非常清晰的关键过程模式

实例的作用真的那么强大?这一关键过程模式是否有效?我基于本书的结构组织了一个主题为ATDD[1] 的workshop,邀请到了很多产品负责人、测试人员、程序员以及项目经理的参与。我把大家分成几个组,各组都包含不同的角色,简单介绍了实例化需求的基本概念和目的之后,我挑选了书中的一个简单例子,给出商业目标和范围,让各个小组协作制定需求说明、举例说明、进一步提炼并使用Gherkin语言[2] 描述。一个半小时之后,最终的结果超出了大家的预期,所有组都认为对于需求大家达成了清晰的共识,很多人总结时都说体验到了实例的强大,编写代码自动化验证这些实例看起来也不是什么难事,还有一些人则饶有兴致地讨论起用哪一种工具实施自动化更有趣。

有了这样的经验,我就更有信心给所有软件开发从业人员推荐《实例化需求》了。不过对于项目经验缺乏或者是喜欢看代码的人来说本书可能会显得艰涩,在前言里作者就说了:

本书没有源代码,也不介绍任何工具。

因此想了解工具的人需要找其他书籍补充,但缺乏项目经验会比较麻烦,因为书中有大量的案例分析和访谈、涉及了各种各样的角色和场景,这些内容对于有经验的软件从业人员极具参考价值,但也会让新手摸不着头脑。

根据我的经验,很多人,尤其是程序员,在刚接触实例化需求或ATDD的时候,会过多的关注工具而忽于协作。事实上开发人员和用户之间良好的协作远比选择使用Cucumber还是Fitness来得重要,不要忘了,我们更应该关心是如何让F-16战斗机能够更容易从战斗中逃脱,而不是花上很大的代价非要让它的飞行速度达到2.5马赫。

[1]: 你可以大致把ATDD理解成实例化需求的另一个名字,反之亦然。
[2]: Gherkin语言是实例化需求工具Cucumber的一部分,通过及其简单的Given/When/Then语法及表格帮助描述业务人员能轻易理解的需求说明。

本文已经首发于《程序员》2013年3月刊,版权所有,如需转载,请务必附带本声明,谢谢。


原创文章,转载请注明出处, 本文地址: http://www.juvenxu.com/2013/04/15/the-power-of-specification-by-example/

Categories: , 敏捷 Tags: , , , ,

哪来的编程天才?

March 28th, 2013 3 comments

《哪来的天才?》是一本值得一读的书,书的核心观点大致为“卓越的能力并非天生,而是后天长时间的刻意练习而得的”,这观点适用于所有领域,包括体育、音乐、医学、商业等等,当然,放到编程领域来说也适用。书中有大量的案例来佐证作者的这一观点,包括家喻户晓的莫扎特和老虎伍兹,这里我不再赘述。基本上,我是认同作者的观点的,那么接下来的问题是,何为‘刻意练习’?如何去做?

作者说刻意练习:

  1. 它是为了提高绩效而被设计出来的。
  2. 它可以反复很多次。
  3. 对结果的反馈是可以持续得到的。
  4. 这是非常费神的。
  5. 这并没有多少乐趣。

在某些领域,这很好理解,比如我曾经练过一段时间的吉他,基于老师指导的练习基本就符合这五条,还有运动员的练习,也完全符合。回过头来看看编程领域,存在这样的练习吗?

Robert C. Martin在《程序员的职业素养》中有两章与这一话题相关,其中第6章‘练习’介绍了Coding Dojo和Coding Kata等方法,这些方法的确有效,也基本满足上述五条,但大部分的Dojo或Kata基本都基于小型问题的练习,而软件开发涉及的领域太广太复杂了,往下到操作系统内存调度,往上到大规模互联网企业的架构,业界基本无法提供针对这类技术的练习,大家只能在实际工作中积累。该书中的第14章“辅导,学徒期与技艺”强调教导的重要性,这非常暗合上述的第1和第3条,导师能帮你设计适当的练习(这也可能是实际工作),同时给你反馈。但不幸的是,正如作者所说,我们的行业严重缺乏这样的导师机制。

除此之外,我认为结对编程也是一种行之有效的刻意练习,这种编程方式非常费神自然不必说,实践过的人都能体会,这里的关键是,通过结对编程这种工作方式,你能持续地得到反馈,这对于提升技艺来说无比重要的。

同时我也发现一个不幸的事实,那就是大量简单的阅读对提升技艺其实并没有很大的帮助。阅读如果缺乏明确的目标(读完一本书不算个明确的目标),如果不去反复咀嚼,那效果就甚微了,更何况大部分时间我们都是独自在阅读,根本得不到任何反馈。有些教材类的书会有习题和答案,那是一种很好的练习及获取反馈的方式,但工作以后,行业中这类书几乎没有。有一种充分发挥阅读功效的方式是小组阅读,一组人读同一本书,定期分享心得体会,如此,反馈就比较充分了。

Robert C. Martin并不是唯一一个认识到刻意练习价值的编程大师,Andy Hunt在他的著作《程序员的思维修炼》中也花了大量的篇幅讲主动学习,介绍了很多具体的方法和工具,例如SMART目标(Specific, Measurable, Achievable, Relavent),SQ3R阅读,思维导图等等,这些内容都非常的有价值。Andy还提到说教学是学习的一种很好的方式,这一点我深有体会,我写《Maven实战》的过程就是一个不断学习的过程。

我们中的大多数人不大可能成为世界级的程序员,其中之一的原因是,我们之间的学习练习积累已经远远比不上那些从小学时就编程的人,另外一个原因则是,我们也不会年复一年的去坚持大量的学习和练习,这有性格的因素,也有现实的因素。但是,这也不代表我愿意随波逐流,一事无成的过一辈子,我总希望自己有所成就,即使不大,但至少会让生活充实点,让家人生活更好点。因此我们不必非要从成功学的角度来读这本《哪来的天才?》,但我也很讨厌某些人动不动就来“一万个小时”,奶奶的,老子只是个凡人。


原创文章,转载请注明出处, 本文地址: http://www.juvenxu.com/2013/03/28/programming-talent-is-overrated/

Categories: 总结 Tags: , , ,

变化是如何产生的——有关结对编程

January 10th, 2013 No comments

一个十多人的团队,十个月前,每个人都习惯在自己的一块自留地代码上劳作,当管理层推结对编程时,大部分人反应冷漠甚至抵触。今天,这个团队的几乎所有人都愿意接受结对编程这种工作方式,各个人对整个系统的了解更好了,遇到不熟悉的地方都很自然地想到找个熟悉的人来结对,很自然的,团队合作更好了。然而,同样的公司,不是所有的团队都有这样的变化,自留地式的工作方式在一些团队非常普遍,这是为什么?

直接管理者的支持

直接管理层就是大家直接的老板,如果是再上一层,就算他支持,如果和直接管理者的理念不一致,那也比较麻烦。管理者的理念差别很大,有人喜欢微管理,事无巨细都要了解并把握方向,为了短期效率让每个人专注在一小块代码;有人喜欢放手让团队自己思考协作,让大家多花点时间去了解别人在干什么,以让知识留到更多人的脑子里。当然这也不是非A即B的事情,但更偏后者的,自然也就更支持大家结对编程;如果偏前者,那就算有开发人员想结对,也会承受一些压力。因此很可能出现这样的现象:高层推敏捷转型,底层积极性也很高,中间被卡了一下,进展就变得磕磕绊绊。

少数的 Early Adopter 及 Coach

每个团队基本都会有一两个人对变化感兴趣,或者更好的情况是本身就很相信包括结对编程在内的敏捷实践,那么这些人在管理者支持的前提下会对整个团队产生持续的影响。与之类似的是我目前从事的角色,Agile Coach,在团队中推广并实践结对编程,解释why、总结how,诸如此类…… 给我印象比较深刻的是,一个原本对结对编程抱怀疑态度的开发者,在和一起结对了几个session之后,明确地告诉我喜欢适当的结对,并在之后团队开会的时候抱怨团队的结对编程不够,这也算是我做 Coach 后感觉比较有成就感的一件事情。这里有个比较重要的原则是,与其把推动变化的精力平均地分散到每个人身上,不如先关注 Early Adopter,他们会帮你扩大影响

代码基的大小

代码基越大,推行结对编程就越难,这是因为更难看到成果。为什么大家喜欢结对编程?因为它能帮助我们高速地从他人学到知识,并且在日后使用这些知识并很直接地体现我在这个团队中的价值。当知识点不大的时候,和人一起结对一两天就能基本掌握的时候,获得成就感非常容易!可是,当一块知识牵扯的代码动不动就成千上万行的时候,学习起来就不是一两天,甚至不是一两个礼拜的事情,这样,还没等你能够使用这些知识去体现价值的时候,很多人就已经对你失去了耐心,甚至你自己也感到沮丧。这也侧面印证了另一个道理,只要是软件项目,代码是一切之本,如果代码写不好,再好的流程也没太大的用处

适当的强迫力及奖励

这简直就是‘胡萝卜加鞭子’,但小心使用也会有不错的效果,尤其对于一些主动性不够强的人来说,强迫至少让他去尝试,奖励也带来了趣味。一些啤酒可乐就容易让可爱的程序员们 high 起来,结对最多的人给个5、6瓶,其他人只有一瓶,或者设定一个团队结对时间总目标,达成了大家就一起分享,都是挺简单有趣的方法。当然大家都不是傻子,前提还是大家基本理解并认同结对编程这种工作方式的价值,强迫或奖励只是帮助去除人天生惰性的辅助手段而已。

看到了一个团队的变化以及结对编程给这个团队带来的受益,我很自然地更推崇这种工作方式了。一些理论上东西我已经在结对编程地价值及注意点一文中简单讲述,但理论再好也终究只是理论,要相信,体验是必不可少的。也许有人会说,“你作为一个敏捷教练,屁股决定脑袋,自然就推崇结对编程这一敏捷实践”,但这只是表面的,如果自己不亲身实践体验并得到好处,我还真不敢就因为看了几本书就与人大吹特吹,如果自己都说服不了,有什么资格说服别人呢?

推而广之,敏捷也是,大家都在谈,有人切切实实实践了,有人纯粹是转发而已。


原创文章,转载请注明出处, 本文地址: http://www.juvenxu.com/2013/01/10/how-change-happened-about-pair-programming/

Categories: 敏捷 Tags: , , ,

编写可读代码的艺术

October 21st, 2012 1 comment

Snapshot 10 21 12 7 42 PM 6

在我们刚开始学习编程时,老师会教我们使用正确的语法、合理的数据结构、高效的算法。而这里的“正确”、“合理”及“高效”,基本都是从机器的角度来说的。但从一般人的角度来看,特别是对非专业人员来说,很多编程语言的语法以及大多数的数据结构和算法都是非常难以理解的。我们可以理直气壮地说“非程序员不需要理解这些东西”,可是我们有没有认真想过,我们编写的代码对于其他程序员来说真的很容易理解吗?

大多数的计算机教育基本只关心代码对于机器的友好性,而不关注代码对于人的友好性。如果代码写完就扔给机器运行,再也没人去阅读和修改,那就没什么问题。可现实情况是,代码会被他人阅读和修改成百上千次。这时,难以理解的代码会浪费程序员大量的时间。在我目前工作的项目中,存在着数百万行的遗留代码,这些代码中的绝大部分都非常难以阅读:变量不停地变换着名字、if语句嵌套个五六层、一个数百行的函数有着数不清的职责……当需要做改动时,程序员不得不花大量的精力去试图理解这些晦涩的代码,而且就算这样也会经常不小心踩到某个陷阱,这种情况非常影响程序员的士气。

因此,代码应该是易于理解的,这正是《编写可读代码的艺术》一书的主旨。使代码变得易于理解的方法多种多样,可以使用DSL(领域特定语言),可以合理使用设计模式,可以使用TDD(测试驱动开发)。相比之下,本书描述的方法看起来最为简单,简单到方法的命名、注释的编写、if/else语句的组织……不过这些看似简单的方法其实很不简单,因为这些细微的地方太难觉察了。

本书的一大特色就是用大量短小的代码说明问题而非长篇理论阐述,每个话题都相应地配有未考虑可读性的代码和考虑可读性的代码,非常便于对比和体会。你可能会认为即便考虑过可读性之后,代码看起来也没有发生太大的变化,无非就是一个变量的名字、一条注释的语言组织、几行语句的结构顺序,不足为道。但我认为这些细微的变化恰恰就是对“艺术”二字最好的诠释。精益求精地编写每一行代码,让他人在阅读时感到心情愉悦,积跬步以致千里,不正是每个程序员应该追求的职业态度吗?

本书在第14章有一个可能会有争议的地方,作者为了使单元测试用例的代码能够尽可能简洁地表达意图,而创建了几个包含很多逻辑的测试脚手架方法。也许有些人会觉得这样的牺牲是值得的,但有些人则觉得这是一种过度设计。这就仁者见仁,智者见智了。

本书每章都包含了许多有趣的漫画,所谓一图胜千言,这些漫画能让你在一笑之余更深刻地理解作者想要表达的意思。本书的封面也非常漂亮,对得起“艺术”二字。如果你希望写出可读性更好的代码,本书值得细细品味。本书在国内有翻译和影印两个版本,大家可以根据自己的喜好挑选最适合自己的版本进行阅读。

本文已经首发于《程序员》2012年7月刊,版权所有,如需转载,请务必附带本声明,谢谢。


原创文章,转载请注明出处, 本文地址: http://www.juvenxu.com/2012/10/21/the-art-of-readable-code/

Categories: Tags: , ,

另一种持续集成策略——延迟提交

October 12th, 2012 2 comments

常见的持续集成策略是这样的:开发人员修改本地,然后执行本地构建,如果本地构建通过则提交变更至源码服务器。当然提交的时候可能遇到冲突需要合并,那么合并完成后需要再次执行本地构建并确保其通过。

在这种传统的策略下,开发人员执行本地构建是必须的,这样很多问题就能在本地得到快速的反馈,并且不会干扰其他开发人员的工作。试想一下,如果每个开发人员每天提交一次破坏构建的变更到源码服务器,7个人的团队一天就是7次,那持续集成服务器一天的状态几乎都是红色了。事实上,即使所有人都执行本地构建,持续集成服务器有时候还是会出问题,这些问题主要是由开发机器和持续集成机器的环境不一致引起的,当然,一般来说出现这样问题的几率不会太高。

现在,假如因为某些原因,开发人员没有在执行本地构建就直接提交变更到源码服务器了,这时面对千疮百孔的持续集成服务器状态我们应该如何面对?

开发人员不执行本地构建的因素可以有很多,例如:

  • 构建规模太大,本地构建需要等待的时间太长。
  • 项目规模比较大,不是所有开发人员都能很好地遵守这个习惯。
  • 本地缺乏必要的构建环境。

理想情况下我们应该解决根本的问题,例如让开发机器的环境和持续集成机器的环境一致、让集成的时间变短、培训大家有持续集成的意识等等。下面我要说的是,当上述问题不能够得以简单解决的时候,可以考虑的另一种持续集成策略,那就是延迟提交。

简单来说,延迟提交就是让代码变更在被提交进源码仓库之前,先由持续集成服务器执行一次单独的构建,只有在这次构建成功之后,才让代码变更真正的进入源码仓库,进行真正的集成。

这么做相当于把本地构建的工作转移给持续集成服务器,但不论如何,变更进入源码仓库之前都需要一层构建保障。延迟构建如何实现呢?基本上有这么两种方式:

  1. 把变更集直接发送给持续集成服务器,持续集成服务器为其执行一次单独构建,构建成功后把变更集提交进源码仓库。
  2. 开发人员提交代码至一个单独的源码分支,持续集成服务器监视该分支,发现有变更则执行单独构建,构建成功后把变更合并到主干分支。(合并可以由持续集成服务器自动完成或者由开发人员手动完成)

下面让我们以TeamCity和Git为例来看一下如何实现第二种方式。(之所以不使用更流行的Jenkins而是用TeamCity是因为我发现TeamCity对延迟提交的支持更好,详见 http://www.jetbrains.com/teamcity/features/delayed_commit.html 。)

首先,除了Git的master分支,我们还需要一个单独的分支来做延迟提交:

delayed_commit_1.png

起名为 remote-run/exp1 是为了符合TeamCity的命名规范,TeamCity有一个名为 Branch Remote Run Trigger 的东西监视特定的 Git 分支,而它默认的模式就是 remote-run/* :

delayed_commit_2.png

当我修改 remote-run/exp1 分支的内容,例如破坏了构建,这时显然不会对 master 产生任何影响,因此一般人查看TeamCity页面的时候不会看到任何变化。然而,当我自己以用户 juven 登陆 TeamCity (我的Git提交 username 也是 juven ),就能从 MyChanges 页面看到一个失败的构建:

delayed_commit_3.png

从图中能看到这次构建是基于 exp1 分支的,点击页面中的其他链接我能得到进一步的信息,例如文件变更diff,构建日志等等,这都能帮助我分析构建失败的原因。上图中给我们还能看到另一次名为 fixed 的构建,其对应的颜色是绿色,那是我之后又在 exp1 分支进行了提交,这次提交修复了构建。

最后,在确认我的几次变更没有问题之后,我可以安心的把这些变更合并到 master 分支:

delayed_commit_4.png

这会触发 master 分支的集成,这个时候的集成当然也非常安全了。当然这样的合并大部分事件持续集成服务器应该也能完成,虽然目前 TeamCity 不支持,但也在计划中了

整个过程其实也不复杂,当然这里有个前提,就是持续集成服务器需要提供很多能够执行单独构建的 Agent ,如何自动化管理这些 Agent ,让它们的环境保持一致,这不是简单的事情。TeamCity 服务器其实只是个调度器,具体的构建工作都交由 Agent 完成,这一点,换成 Jenkins 也是差不多的原理。

除了这种用单独分支的方式,我们也可以直接发送变更集给持续集成服务器,TeamCity 的实现方式使用 IDE 插件,具体信息可以参考 TeamCity 的维基页面

其实有了像 Git 这样创建分支特别容易的版本控制工具,在 Jenkins 上用第一种方式实现延迟提交也不会比用 TeamCity 困难到哪里去,但我还是比较惊讶于 TeamCity 的易用性,这一改了我以前对商业工具软件的偏执看法,当然,这也就是不是本文所讨论的范畴了。

最后要提的是,如果你的项目不大,代码不是特别多,人员不是特别多,基本用不到延迟提交,应该把关注点放在快速集成、保持开发环境与击穿环境一致、以及良好的开发习惯上面。


原创文章,转载请注明出处, 本文地址: http://www.juvenxu.com/2012/10/12/delayed-commit/

Categories: 持续集成 Tags: , ,