代码和设计是如何一步步腐化的

Unimen 2020-06-21

经历了几个从商业角度来看或成功或失败的项目,都会发现代码、设计都会慢慢地、在不经意间腐化。而且有一个项目开始的时候,架构是经过精心设计的,也有较为严格的代码规范,并且通过静态代码检查来尽量保证代码的质量,连code review都有一个可供参考的checklist。但半年一年之后,还是会发现,很多代码都已经臃肿走样,到处都是复制粘贴,动辄好几千行代码的模块,能 work、但不 right的代码。

getting it work is easy
getting it right is hard

不禁想问问代码和设计是如何一步步腐化的?

本文地址:https://www.cnblogs.com/xybaby/p/13173047.html

代码如何开始腐烂

其实大家都听说过 clean code,但不一定真正意识到其重要性,且知道并不等同于做到,而时间更是一把杀猪刀,让程序员秃了,让代码烂了。

一个新项目开始的时候,大家都是满怀壮志,期待灵活可复用的架构,期待成功的产品。与此同时,敏捷开发告诉我们不要过度设计,当然,本身也是很难预料到以后需求变化的方向,于是应该等到第一次变化的时候才去考虑如何重构以应对这一类型的变化。但问题很可能就会出现在这里。

也就是说,也许哪一天,当我们需要加一个新功能的时候,会发现原来的设计和代码不是很方便增加这个新功能。当然,我们不应该过多苛责之前的设计,因为以前没有预料到这个新功能,也就没有在这个地方引入抽象。这个时候有两种解决办法:第一种是重构术,就是加功能之前先了解、重构已有的代码,比如调整一下类的基础体系、抽象出基类、或者引入一个间接层以隔离变化。另一种则是修补术,在现有的函数中加一个 if-else(或者 switch case)、在现有的类中加几个特殊字段。这两种方法都能解决问题,修补术治标,重构术治本,但显然,治标来得更快,治本对程序员的要求更高。

什么时候程序员会选择修补术而不是重构术呢?

也许这个程序员看过 clean code、refactor,精通设计模式和面向对象,也非常希望维护一份漂亮的代码。但我们知道,重构是需要时间的,而且还可能引入bug。也许重构耗费的时间就超过了用修补术 workaround 的时间,就短期来说,修补术的性价比是更高的。那么长远来说呢,也许重构术的性价比更高?可是只顾眼前、及时行乐是人的本能,走捷径、偷懒是无时不存在的诱惑。当然,也许有追求的程序员会抵制这种诱惑,但是社会心理学告诉我们,在压力、干扰面前我们很难理智思考,自控力也会失效。时间、进度压力就是垂悬在程序员头上的达摩克利斯之剑,这压力可能让人失眠、让人头秃,写点垃圾代码似乎也无可厚非。

况且,重构还可能引入bug,重构的前提是要有完备的测试机制,单元测试、功能测试、集成测试一个都不能少。可是,理想很丰满,现实很骨感,单元测试覆盖率往往不足,而且还可能依靠手动回归测试。把代码重构好了可能压根没人知道,没人来感谢你、给你点个赞,但万一重构出了bug呢,大家都会收到事故报告,说不定还会影响KPI?不求有功但求无过,Leader、经理是否认可重构的价值,也很大程度影响组员对于重构的积极性。

当然,增加新功能的也许是一个新手,新手加入团队后,一般就是从维护某个模块,实现一些小需求入手。新手有可能水平本身就不行,而且业务逻辑和代码都是陌生的,如果缺乏完善的文档以及足够的掌握,新手是万万不敢重构的,修补术是最自然的选择,复制、粘贴、稍微修改一下、build、run,成功啦!又实现了一个需求!你知道,新人是急于证明自己的,快速的实现一个又一个需求是证明自己的最佳办法。

你有可能说,新人不是应该有个导师吗,导师得review新人的代码啊。首先,导师得懂这一块业务;其次,导师得愿意花时间指导新人。指导新人是否影响导师的KPI呢?带好了是否有奖,出问题了是否有惩?如果全凭导师自律,这个不确定性就太大了。

上面提到的是新人,其实老手也可能写出“德不配位”的代码,比如一个需求,可能涉及到多个模块,有的模块是这个老手负责的,有的则不是。理想的情况下,各个模块提供好接口供老手调用即可,但某个模块的负责人很忙,没有时间,这个时候老手就会直接去修改相应模块。可是,可能由于老手特有的自尊、或者面子,老手往往不愿意去请教对应模块的负责人,而是按照自己的经验魔改出一段可以工作,但既不优雅、也不高效的代码。

代码如何加速腐烂

所以说,由于进度压力、经验、态度等各种各样的原因,代码中慢慢就会开始出现腐朽的问题。可怕的是,垃圾的代码给出了错误的示范,这种示范对于新手或者对于这个模块不熟悉的同事来说都很强烈,也使得垃圾的代码、倍增的维护成本、潜在的bug被到处复制,美其名曰“借鉴”。破窗效应,让后来人写出垃圾代码的时候毫无心理负担,“以前就是这个样子的”,以前这里有个变量叫temp,我只是加了个变量叫temp1;以前这里就有switch case,我只不过加了一个case;以前的代码就很难读懂了,于是我copy的一份实现自己的逻辑。

况且,到项目后期,可能不再那么挣钱了,可能最初写代码、制定规范的人已经不再了,谁还会来关心这代码质量呢?

悲观的认为,代码的腐化是必要,只是时间快慢问题。

相关推荐