X.D 笔记X.D 笔记

出现 “屎山代码” 的客观原因

出现 “屎山代码” 的客观原因

屎山代码

对程序员来说, “屎山” 这个词,定不陌生。通常指的是质量低劣杂乱无章难以维护的代码。

产生屎山代码的原因实在太多,提到这词,每个人能都有吐不完的槽。大多数原因会指向两个原因:水平太菜、没有程序员的基本修养。

今天作个前提假设:程序员的技能水平没有问题,而且还是敬业爱岗,不会摆烂摸鱼,消极怠工

就算基于这个前提,也阻挡不住屎山的出现,而且原因依然各种各样,今天只考虑两个因素:需求工期代码质量

需求工期 x 代码质量

需求工期就是给程序员写代码的时间,代码质量是程序员最终写出的代码的质量。这里使用一个四象限来解释,如图:

开发周期代码质量符合/超出预期开发周期 代码质量 1用户等不及开发周期  ,代码�质量 2高投入,产出屎山开发周期  ,代码质量 3技术债,屎山开发周期 代码质量 4
  1. 第一象限,做的又快又好,只要不是干活的人都会自然认为理所当然,但真正能把事做到又快又好的团队真是凤毛鳞角。
  2. 第四象限,写的,但代码质量,在经常需要赶工、救火的团队经常出现。
  3. 第二象限,写的,但代码质量,技术人通常希望多点时间,做好一点。但客户关系决定是否给你时间。
  4. 第三象限,“做了这么长时间,还做成了一幅屎样”!双输结局

二四象限

在软件工司,特别是“乙方、外包”呆过的,都很容易理解第四象限,因为技术人员总是被用户、销售、项目经理不停催促,而且时间也会被层层压缩。

每个需求都是那么火急火燎,总是能听到这样的话:怎么快怎么来这里加个判断就OK了先前有过类似的,COPY过来调一下就行 之类。

其实,听到这些话时,说这些话的人其实已经预设了两点:

  1. 这个需求很简单,也不会再变了,改一次就能OK。
  2. 渡过眼前的苟且就行。如果有烂代码,后面技术会抽空优化。

并没有讽刺意味,而且做为服务业,这种思路本身也没有不对,最后变成屎山主要是因为,总以为能后续能抽空优化的烂代码,几乎没有任何后续,积少成多,终究会导致代码变成屎山,无力回天。

第二象限恰恰相反,通常出现在技术主导的公司,通常技术人员都有点“代码洁癖”,看到不合理的地方就想重构,最终没有产出屎山,但商场如战场,大部分客户都不会认可为这些看不见的东西而拖时间。

第三象限

相比二四,第三象限可谓是没有赢家的博弈,即没有按时完成,而且代码也是质量低下,是一场没有赢家的博弈。

开发周期代码质量符合/超出预期开发周期 代码质量 1用户等不及开发周期  ,代码质量 2高投入,产出屎山开发周期  ,代码质量 3技术债,屎山开发周期 代码质量 4

更可气的是,最差的结局还更容易发生,不此是IT界,经常看到很多工程延期再延期,终于完工发布后才发现做出来一坨屎,相比预期简直是天地差别。

造成这种结果的原因也是多种多样的,但有一点相同:无论是哪方,都不会觉得自己有问题,总是有理由把锅甩给其它人

总结几个客观原因:

系统设计超出需求

系统设计超出需求 是接下来其它原因的前提原因,最为重要。

当需求在架构设计考虑范围内时,往往能声笑语,一片和谐。但如果超出范围时,如果耍小聪明或是敷衍了事,就迈出了走向屎山的第一步。

克服的方法就是重构系统设计,让系统能适应新的需求,但是很不幸,基本上很难实现,最终多数以小聪明或是敷衍了事解决,主要原因:

  1. 系统最初始的设计思路,及如何扩展已经无人知晓,或者说干脆就没什么设计思想。
  2. 重构系统设计,需要更多的时间和金钱成本,大部分用户无法理解一个简单的需求也要重构设计,甚至会认为技术不行或是趁机揩油

举个不恰当例子:在古代,开国皇帝往往英明神武,不仅聪明绝顶,能构思出一套管理制度,而且勤劳能干,根据这套制度,只需要比较精简的一小群人就能完成对国家的管理。但到王朝中后期,这套制度可能已经满是补丁,围绕这套制度的官员数量已经几十倍于开国时期,运行效率极度低下,但也动不了,凡有人想变法改革都会被祖制不可弃为由遭遇极大反弹,少有成功。

软件的生命周期也颇为相似,与普通人的想法不同,从0到1的做一个新软件其实是相对简单的。而后期所有的需求、特性,都会基于前面搭建的架子了,当初创作时的思路扩展性会极大影响后面的需求实现,但通常想法就是不一样:

  • 软件的创建者和后面的维护者通常不是一个人。
  • 即便同一个人,时间一长,忘掉之前的创作思路是必然事件。

软件属于服务业,技术好坏并不仅仅取决于最开始创作出来时的功能特性,更重要的是后续维护,是否更容易扩展。好的软件加一个功能一两天甚至一两小时就可弄完,弄完后也会自成一体,基本无BUG。但有些加一个同样的功能动辄数周甚至数月,加了之后BUG频出。

经济考量因素

虽然事事不能谈钱,但钱真的是一个很大的因素。

软件一旦初版发布,多少都有些祖制不可变的思想,一般行业可能问题不大,但在软件业,就有点尴尬。

技术变革实在是太频繁了,几年时间就会迭代,不仅是技术框架,甚至开发语言、开发范式、开发工具都会被淘汰。

这可能会产生一种屁股决定脑袋的行为,比如国内很多HR部门就要求技术部分只能用Java技术栈,因为人相对好招,而且价位不算高。

随着时代的前进,人才市场上成分也会变化,一些不主流的技术都会因会的人少。从而恶性循环,会的人少后,相关技术也会因维护支持度下降而被其它技术超越,新入行的技术会偏向于学习新技术,找使用新技术的企业。

消费市场的手机,用了几年后都会仍了买新的,但软件公司的产品可能就会很麻烦,换新永远就代表着又要投入很多人力成本,增加一些风险,而带来的收益又是一个风险因素,

对于软件公司来说,甚为尴尬,就好比手机,用了几年后,各项指标都不行了,消费者可以直接扔了换新的,但对于一个企业软件来

软件公司经常会出现销量担当的产品,卖久之后,使用的技术已经不再主流,而维护起来困难度要高,而人才招揽可能会更难而且更贵。到这个地步时,就只面临两个选择,通常后者更常见:

  • 完全推到重来,使用新技术,新思想,耗费非常高
  • 以耗费低的方式苟延残喘,向屎山方向继续前进

教条式敏捷

在程序创建时,创建者的设计理念都会允余一定的扩展空间,用来应对未来的需求变化。但一般应用的需求总是感觉无穷无尽,为了用户第一的口号,很多团队管理者就实施敏捷式开发

敏捷的目标就是为了快速响应变化提高产品质量客户满意度,在我入行时,敏捷相当火爆,几乎一半以上的管理者都多多少少实践过部分敏捷的内容,但少有收益为正者。

敏捷开发可以说是一个先于时代的思想,在早年间想实施敏捷成本是非常具大的,需要耗钱耗时耗力请敏捷教练、代码提交规范、团队规范建设、工具制作等等。

现代的编程范式都源于早期的敏捷思想,更是一个程序员习以为常的日常

  1. Git 已彻底替代SVN、CVS,主流分支规范(GitFlow/GitLab)已满足绝多数项目需求。
  2. 现代的CI、Docker等工具和早年的工具已不可同日而喻,插件丰富,生态强健。
  3. 单元测试就是一个现代编程语言的内置功能,不像以前需要参差不齐的三方工具。
  4. 在从前,微服务云原生等只能想想,现在架构这样的服务也没了技术门槛。

但很多管理者即便刷了几遍《敏捷开发的艺术》也并不明白,教条式硬生搬运,导致力量都消耗在内部流程,技术投入下降,产生屎山。

当然上面的也只是技术因素,而而敏捷推崇的一些其它非技术因素,也不一定适合所有类型类型,比如客户参与自组织团队 等。

简单说来,只要项目团队不是过于老套落伍,拥抱了现代开发的一些基本套件,就已经算是实践了一个还算不错的敏捷开发模式,再根据项目自身情况规范一些团队要求即可,没有太大必要教条式的引入敏捷。

不推荐教条式敏捷主要原因是:没有好的工具能支撑整体规划时,教条式敏捷会偏向使用人力完成,现代化的系统中,如果太多环节依赖人力,那很容易形成兼顾不全,必有一失的局面。

举个例子,我个人比较赞赏CodeReview,但我极度厌恶团队执行CodeReview制度,人类既没意愿,也没能力去完成这项工作,而且很多文化也内化到人本能抗拒这种事。但现在,情况又有所不同了,AI已经能胜任部分CodeReview工作了,如果把CodeReview集成在CI工具中自动完成,那则是充分发挥了敏捷精神。

沟通不畅

只要是沟通,100%存在信息丢失,沟通不畅,用户/销售/经理/技术的语言不通,也是产生屎山代码的重要原因。

《领域驱动设计》)(DDD)一书中讨论了一种以通用语言来降低信息丢失的一些手段。但真要实行还是很困难的,特别是名词一套一套,而且DDD也不是给普通程序员的指导书,对管理或是决策者要求更高。

就像早期的敏捷一样,DDD提出的一些模型和思想现在只是听起来很美,实践起来需要极大的勇气,有部分反常识和晦涩的内容,早年行业景气时还有敏捷教练可请,现在也没听说过有DDD教练。

同样,时代的进步也部分解决了这一难题,技术方面,现在的原型工具、协作工具远胜于早些年,很多企业软件也甚至能充当外援。

当然这只是技术方面,非技术方面的制度流程、需求交涉仍然如同从前。但DDD也有一些思路可以做到有限的进步,比如内部实行领域语言、模型分层等。

小结

虽然技术在不段变化,但实际上,架构思想变化也并不多,而且很多早先的理念,比如高内聚,低耦合开放-封闭原则等等,相信每个程序员都会背,但真正能秉持的少之又少,总是在写代码的过程中能找到各种理由不遵守。

当然了,按存在即合理原则,屎山的存在有诸多因素,不是人力故意为之。见过的屎山多了,吸取教训也是一种进步,也并不是所有的屎山都值得抢救。

当然了,要想防御或是有所行动,可以如《重构:改善既有代码的设计》所说,把重构当成一种日常,不段提高代码的可读性、可维护性和灵活性。

以下几本书都比较难度,但是里面的思想都还不错,在中国这个环境可以说就算学点皮毛也能胜过不少程序员了。

  1. 代码整洁之道 - Robert C. Martin
  2. 重构 改善既有代码的设计 - Martin Fowler
  3. 领域驱动设计 - Eric Evans
  4. 设计模式:可复用面向对象软件的基础 - Erich Gamma