“一个房子如果窗户破了,没有人去修补,隔不久,其它的窗户也会莫名其妙地被人打破;一面墙,如果出现一些涂鸦没有被清洗掉,很快 的,墙上就布满了乱七八糟、不堪入目的东西;一个很干净的地方,人们不好意思丢垃圾,但是一旦地上有垃圾出现之后,人就会毫不犹疑地抛,丝毫不觉羞愧。”
我们一直在喊敏捷开发,其实敏捷开发的一个很重要的目的就是消除浪费,防止破窗效应的发生。事情太难,就让它简单,更简单。流程太重,就让它轻点,更轻点。尽量扫清开发的障 碍,消灭破窗形成的环境。下面我会从软件构建的很多方面来描述如何防止“软件开发中的破窗”。
脏代码
如果代码不整洁,后来人就很难看懂,人们往往会对难以看懂的代码失去耐心,不愿意进一步了解。如果不能进一步了解一部分代码,也就难以改进它,这样 带来的一个后果可能有两点:1、这段代码被抛弃,然后重新编写。2、直接复制这段代码在别的地方使用。对于第一点,会带来软件开发中的浪费,而且再次编写 也不可能就能一部到位的编写正确,可能会引入新的bug。对于第二点,大家都知道重复代码是设计走向腐化的根源之一。
如果我们在编写代码时能不断的应用一些原则,确保我们的代码易懂,自描述。在开发新特性时还不断的使用重构手段,让我们的设计保持一个良好的状态。 我们就能防止窗户被继续打破。
测试
没有测试,或者混乱的测试代码都是破窗滋生的环境。
没有测试
没有测试时,当我们想对一块代码进行重构,我们就像没有带保险绳走钢丝,步履维艰,生怕一下子失去平衡,掉下悬崖。这样在我们的心中就产生了惧怕重 构的阴影,久而久之,我们就不去重构。最后带来的结果就跟上面一段说的一样,设计不断的腐化,然后就失去了控制。
如果有了单元测试,有了验收测试,当我们每做一下重构时,我们都可以从测试快速获得反馈,每当红条亮起时,我们知道我们破坏了一些已有的功能,我们 停下来去修复,当绿条亮起时,我们知道现在处于安全状态,可以安心的继续重构。一切都在我们的掌控之中,我们会喜欢上重构。
混乱的测试代码
有很多人觉得测试代码不是交付给用户的产品代码,可以区别对待,我们不需要花那么多时间琢磨变量命名,方法命名,我们也不需要关注重复的代码。但 是……
混乱的测试代码跟没有测试是一样的,甚至比没有测试更糟糕。我们以为我们有测试,但测试却给我们虚假的报告,当我们发现我们的重构破坏如此之深时, 已经为时已晚。即使测试能给出真实的报告,但如果测试代码混乱,那么添加新的测试就非常困难,我们就会越来越惧怕添加新的测试。而且随着产品代码的演进, 测试代码也需要伴随着演进,测试代码越混乱,我们就越难以修改测试,让它反应出现在产品代码的状态。终于到了一天,大家决定抛弃测试,如是我们又回到了没 有测试作保障的日子。
实际上,从某种程度上测试代码的整洁程度比产品代码的整洁程度更重要,因为有了好的测试我们可以无忧无虑的重构我们的代码,即使现在我们的产品代码 很糟糕也不怕,因为有了测试的保证,我们知道我们可以重构过去,如果我们只有混乱的测试代码,我们那一线重构的希望都没有了。
难以测试
可测试性是衡量代码的一项准则。既然是准则一般都很难达到,如果代码难以添加测试,在尝试几次之后,我们一般都会放弃编写测试的想法。当我们尝试对 一段代码编写测试时发现,这块代码铁板一块,与太多的其他类耦合,需要传入很多重型对象的参数,比如与设备交互的代码,与数据库交互的代码相耦合,这些重 型对象很难模拟或插桩。没有办法,在进度的压力下我们只有放弃添加测试的想法了,那么如上面一样,代码就像草原上奔跑的野兽,失去了控制。
编写可测试性的代码是困难的,要将糟糕的代码改进成可测的代码尤其困难。但有一个诀窍,我们可以先编写测试,用测试驱动出我们的产品代码,这样一开 始我们就获得了一个个测试套件,将我们的产品代码稳稳的固定在那里,就像走钢丝时的保险绳;不仅如此,我们还获得了可测试性的代码。
测试运行太慢
实际上测试运行太慢是一种信号,该信号告诉我们耦合的太紧了。运行一个测试,需要编译加载很多模块。如果运行一个测试需要20分钟,你希望频繁的运 行测试么?如果运行一套测试需要10个小时,你希望测试多久运行一次?测试运行太慢就是第一个被打破的窗户,如果不赶快修补,后面会有更多的窗户被打破。
测试运行太慢,我们就不会频繁的运行测试,测试也就不能提供立即的反馈,这样测试的作用就大打折扣了。
上面主要从代码实践方面来阐释编码中的破窗和如何防止破窗,其实在软件开发的很多方面都存在类似的情况。
源代码管理
有很多团队因为各种各样的原因采用了难以使用的源代码管理工具,或者完全因为厂商对管理层的广告宣传,采用了一个无比重型,好看但不中用的工具。在 经受一两次工具的折磨之后,团队成员就会产生惧怕的心理,尽量的推迟提交代码。提交代码需要足够的频繁,甚至一次有意义的重命名都可以作为一次提交,这样 在代码复查的时候光阅读提交代码的注释就能演示出代码的演化过程。而且,如果每一次成功都有保存,这样在犯错的时候我们有机会后悔,我们有机会回滚到一个 成功的状态。人的大脑虽然非常聪明,但也非常易于出错,特别是在疲劳的时候,如果我们小步前进,小步提交,我们就能停在任何地方。你还记不记得那种必须到 某个时候才能保存当前状态的电脑游戏?
有的时候并不是工具难以使用,而是环境使然。在分布式的团队里,有可能网络不稳定,远程源代码仓库经常不可访问,或者在提交代码时需要连上VPN, 然后再提交,久而久之也会让团队成员懒于提交代码。这样我们就应该采用分布式的源代码管理工具,比如Git。
难以集成
代码写完了并不是开发任务的结束。你还记不记得多少次为了集成产品,解决几个模块之间的冲突而加班加点。敏捷强调及时的反馈,持续的交付。如果集成 一次产品需要几天时间,我们如何做到及时反馈呢?如果集成太困难,大家都会惧怕集成,就会尽量的避免集成,但产品最终是要集成的,所以到了最后期限的时 候,大家都在加班加点,但却不是写代码,而是为了集成。
如果集成太困难,我们为什么不持续的集成呢?所有团队成员都工作在同样的分支上。持续集成服务器不断的签出最新的代码,运行各种各样的测试,最后构 建出可用的软件出来。只要需要,任何时候我们都可以提供可以工作的软件。
可视化
可视化是管理中的铁三角之一。很多管理人员喜欢使用各种各样绚丽的工具绘制出绚丽的图表。比如使用Project做出精确到天的人员计划,使用 PowerPoint做出产品的宏伟蓝图。好像将这些做出来,然后发给大家就有一种这个项目都在我的控制之内的感觉一样。其实不管怎么优秀的软件工具还是 比不上纸和笔。软件打开需要时间,随着软件更新换代,软件体积越来越大,打开一个庞大的Project文件甚至需要一两分钟的时间,而且文档埋藏在电脑文 件系统的深处。找到文档,打开,几分钟就已经过去了,别看这几分钟,久而久之我们就不想再去看这些东西了,我们以为这些东西都装在了我们的脑中,但实际却 没有。花了很多精力编写的需求文档,最后成了一纸空文,当发现与需求不符的时候已经晚了。要防止这种事情的发现,我们就不要打破第一扇窗。
虽然到了二十一世纪,丰田公司还是在很多方面采用原始的看板。软件开发中也是一样,抛弃那些精美的软件吧,将计划,进度,用户故事用最简单的纸和笔 绘制,然后贴在开发人员抬头就可见的墙上。不需要画的多精美,因为越精美就越不想去修改,但软件开发中永恒不变的是变化,我们必须随需而变。
笨重的流程
有的公司给开发、测试、部署规定了严格的流程。开发人员想将产品功能部署到测试环境都需要与很多相关人员交互,提交申请单,然后才能由专人将刚刚修 改的一行代码部署到测试环境中,进行测试。首先不说这个过程中有多少等待,多少浪费。光这笨重的流程就让大家望而却步,进而导致惧怕修改,连好的改进都会 受到抵制。
后记
软件开发的方方面面就像一扇扇窗户,不要打破第一扇窗户,打破了也要赶快去修补,不然软件就会随着窗户一样,一扇扇的被打破,慢慢的腐化下去。
日本的丰田公司,在远远落后于福特和通用之后,采用5S的精益思想【注3】,成为后起之秀,而这5S(整理、整顿、清扫、清洁和素养)最终的目的就是 防止破窗效应。
注1: 经济学上还有一个破窗谬论,但与此文没有关系。
注2 : 要了解破窗效应更多细节可以参见wiki:http://en.wikipedia.org/wiki/Broken_windows_theory
注3: 精益思想(Lean)是MIT对丰田生产方式(TPS)进行研究后的产物。关于丰田5S和精益思想(Lean)可以参看丰田的相关著作。