本文转自公众号:美男子玩编程
“屎山”代码稳定的现象,就像是你能在一个破旧的老房子里安稳地住上一辈子。
你会发现,尽管墙壁斑驳、地板松动,但奇怪的是,这个房子就能挺过风雨,依然屹立不倒。
这种代码是技术负债的结果,刚开始可能充满bug和遗留问题,但随着时间推移,它已经适应了并且“理顺了”许多潜在的问题点,变得有点像一个“躲过风雨的老兵”。
不过,总有一天得面对技术债务的“爆发”时刻,到时再来一场重构也是必然的。下方回答摘取知乎问题“如何看待“屎山”代码却异常稳定?选了一些回答。
因为屎山不是一天拉成的,它是很多人多年慢慢拉成的。
每当新人在山顶拉软软的新屎时,底下的屎已经凝固硬化了,强度经得起考验。
不怕在山顶拉新屎,就怕在山脚铲旧屎。
大家都把屎山说得太好了。说得好像所有,至少大部分弄得出屎山的团队都有特别好的测试环节能保证屎山的质量呢。
事实上根本不可能——反过来说,如果一个团队有非常严谨和良好的测试流程来保障质量,那么他们同样大概率在做需求分析、代码设计以及代码review环节也会做得不错,这样的代码只能称为复杂,不能称为屎山。
屎山代码之所以稳定,最重要的原因是人择原理——不稳定的屎山代码早就因为各种原因(通常是无法快速加功能)而被抛弃了。实际上,每一次“重构”/“重写”,其根源动机大都是因为被某座屎山给刺激到了。
而另外两个常见的淘汰屎山代码的时机是项目结束,和项目切换技术栈。
因为经过了足够的测试。
工程问题就是这样,不依赖于劳动者的高素质,而依赖于工作流程,好的工作流程可以让领域内的大多数劳动者都可以做出质量达标的产品。
软件测试成本远低于其他行业,所以测试驱动的开发被更广泛应用。
我们公司某些有几十年历史的大型软件,哪怕你只修改了一行,提交代码后都会触发自动化测试,把所有用例跑一遍,跑几天都是有可能的。
看着繁琐,但是真省心,只要用例跑完没问题,你就可以相信这代码很健壮了。
自然,你还要补一些针对你的修改的测试用例,然后用例会被几个你不认识的人评审。这样做是为了检查你补充的注释和文档,如果另一个程序员可以不与你做任何沟通,仅凭代码、注释、文档,就能理解一切,说明你的工作满足要求了。
我刚入职的时候,总觉得前人的代码不优雅,总想自己改改,结果无一例外,大改之后连用例都跑不过,总有犄角旮旯的地方会出错,然后我才理解,很多地方不是前人不想改,而是真的没法改。
每当我看到一段代码很不优雅,觉得自己能写得更好,往往不是我很强前人很弱,而是有我没发现的坑,等我把这些坑都踩到了,我会发现,前人的方案才是最好的,我还没人家做得好呢。多年之后,每当我评审别人的代码,往往我修改的,是测试用例。
我会检查他们的用例是否合理,是否全面,只要用例质量够高,能过,代码就不用看了。
罗马不是一天建成的,屎山代码也一样。
也许最初的时候,只是代码不那么优美而已。慢慢的,各种需求驱动下,来来往往的各个水平的程序员的各种质量代码插入,最终形成了屎山代码。
然而,它经历过了时间的检验,虽然烂得让人无法下手修改,但是毕竟是需求驱动的,维持现有业务还是能勉强运转的。
这种情况下,推倒重构需要工作量极大,在上面再绣花也是难上加难。
回到问题,实际上也谈不上异常稳定,只不过是原有业务已经支持了,不改动的情况下,一般不会出什么问题。一旦加新功能,或者尝试重构,那就会极其不稳定。否则,也称不上是屎山代码了。
有一种开发方法论:测试驱动开发。当遍历了足够多异常情况之后,稳定性就来了(不厚道地笑)
比如你要我写一个函数判断给定的整数是不是质数,于是我写:
bool isPrime(int nNumber){
return false;
}
你测试时,发现输入3和5返回结果不对,那行,我改为:
bool isPrime(int nNumber){
if(nNumber == 3) return true;
if(nNumber == 5) return true;
return false;
}
你测试发现之前的bug修正了,但你发现输入11、13、17不对,于是我改……
当你有足够时间去测试,我又花足够时间去改,结果总归会“正确”。同理,当有测试又有修改时,稳定性总是会得到提升的,当时间足够长,环境足够多,稳定性好象也“足够”。
你测试时,很少发现问题,你看到代码时,你想砍人。----很多祖传“屎山”代码大概就是这意思。