系统渐渐沦为“屎山”,这就是真相!

分享是最有效的学习方式。

博客:https://blog.ktdaddy.com/

背景

小猫维护现有的系统也有一段时间了,踩坑也不少,事故不少。感兴趣的小伙伴可以了解一下,往期的小猫踩坑记合集。

这天,小猫找到了商城系统的第一任开发老A开始聊天。

“你们这系统是真坑,我都吃过好多次亏了,太烂了…”小猫开玩笑地吐槽道。

“我们当时其实还是花了很长的一段时间去做设计以及评审的,每一步都是有严格把控的,当时系统总体骨架还是相当清晰的,也没有那么多新的概念,你说现在系统不行了,可不能怪我们啊。一会我给你原始设计文档看看。后面经过了很多研发的手了,甚至运营和产品都换了好几拨了,每任运营可能对当前系统的要求都不一样吧,大家理解可能都不同…”老A侃侃而言着。

小猫抿了口刚倒的茶,意味深长地看着老A…

写在前面

相信有很多小伙伴都有小猫这样的体会,尤其是接手一个老的系统的时候,总是会吐槽当前的系统很烂,恨不得马上将其完完全全重构掉。

前段时间老猫还遇到一个比较逗的小伙伴,他想表达的意思大概是“代码写的烂也就算了,他居然还在注释里撒谎…”,结果他楼下哥们还在一个劲追问他的注释是怎么撒谎的,老猫当时边吃午饭边在刷手机,老猫看到评论后,笑到喷饭,当然在此也对这位小伙伴表示同情。

在这里插入图片描述

其实很多时候一个系统的腐烂和破败并不是在开始的时候就出现了,而是在持续地迭代升级中渐渐腐化继而沦为“屎山”。

接下来咱们就来盘点一下到底是一些什么原因将一个原本架构清晰的系统腐败沦丧为复杂“屎山”的,然后咱们作为后来人又该如何应对?

在这里插入图片描述

“屎山”特征

既然说到系统沦为“屎山”,那么什么样的系统会被定义成“屎山”呢?其实所谓的“屎山”即为非常复杂的系统,难以维护。John OusterhOut在A Philosophy of Software Design这本书中就已经提及了“复杂性就是使得软件难于理解和修改的因素”。

John OusterhOut将“屎山”(这里要说明一下,这哥们并没有说复杂系统叫“屎山”,哈哈,只是咱们都习惯这么叫了)归为三大类:

  • Change Amplification(变更放大)
  • Cognitive Load(认知负荷)
  • Unknown Unknowns(未知的未知)

上述几类特种总结有点抽象,咱们来具体化阐述一下。

变更放大

变更放大其实就是说明明一个相当简单的需求,却要动到很多地方的代码。

相信大家在日常开发中应该也会遇到这样的问题。老猫也经常遇到,例如明明从需求的理解来说,只要加一个固定的校验逻辑,这个事情就应该可以搞定了,结果发现,改一个地方的校验逻辑还不行,可能还要动到各个地方的校验逻辑。这种情况往往是由于开发在写代码过程中复用没有到位,或者本身流程问题导致的。软件可拓展性变得很差。

认知负荷

认知负荷说白了就是相关的开发人员在进行开发任务的时候,需要花费很多时间去学习所需要的知识(当然这里大部分指的是技术知识)才能完成一系列开发任务,于此同时,如果某个知识点没有掌握好,可能会导致未知的Bug。

打个比方,大部分的开发还是比较倾向于自己熟悉的编程语言或者是开发框架,以及中间件的。例如,前后端分离虽然好,DDD虽然好,但是对于简单的内部管理系统而言,明明一个mvc就能搞定的事情,非得搞成前后端分离,加上DDD设计分层。明明一个人天就能搞定的事情,非得搞成三人天,另外的维护者可能还得花时间去研究相关技术,这种盲目追求最新技术增加系统本身实现复杂度就是一种本末倒置的行为。

当然认知负荷有的时候可能也不一定是新技术带来的,也有可能是纯粹的技术实现烂,例如不恰当的接口设计、混乱的命名,还有“爱撒谎”的注释等等。

未知的未知

未知的未知是最要命的,例如,当我们从产品那边得到一个需求的时候,我们甚至不晓得为了完成这个需求我们到底需要修改哪些代码才能完成,当前开发甚至还不清楚相关的业务知识。

这种很多时候体现在咱们接手一个新项目的时候,尤其是项目比较复杂的情况下,并且此时的项目没有任何的技术文档,这种情况下我们往往是抓瞎的,非常被动,即使对代码调整完毕之后心里也还是会没底,涉及的一些业务场景甚至都没有理清楚。也不晓得调整完毕之后会不会出现新的问题。

“屎山”诱因

从技术侧聊聊,复杂系统发展的诱因,UML之父Grady Booch在《面向对象分析和设计》的观点是,软件的复杂性是一个固有属性,并不是偶然属性,软件的发展必然会伴随复杂性。

诱因有下面三个:

  1. 模糊性:模糊性产生了最直接的复杂度。
    老猫的理解,关于模糊性包含其实有两层,一个是需求模糊性,第二个技术模糊性。产品经理对实际的业务把控没有做到很精准,存在模糊性,导致系统本身的业务覆盖点经常发生变更,这种是导致系统复杂的罪魁祸首之一。第二技术侧的模糊性,技术侧的模糊性当然就包含研发人员本身对业务把控不到位,另外的在定义API以及方法命名变量命名的时候存在模糊,无法通过命名直接理解想要表达的意思。

  2. 依赖性:模糊产生复杂性,而依赖导致复杂的传递,不断外移的复杂性将导致最终系统无限腐化,质量失控,修复成本指数级增长。打个比方一个不合理的实现方法被我们认为是一套标准的实现方式,然后后面的很多业务代码为了方便都会去复用这段逻辑。但是这种不合理的实现方法还在不停的迭代,所以之后系统会发展成什么样子,大家可想而知了。

  3. 递增性:一个软件系统无论多么复杂,都是从第一行代码开始的。然后慢慢“生长”。随着业务发展,需求不断产生,功能逐渐丰富,软件系统随之演进,同时废弃而未被及时清除的代码也是日益膨胀。最终形成一个复杂的系统。
    这点相信理解起来还是比较简单的。

系统“腐烂”的真相

就像上面小猫和老A的对话那样,其实很多时候,系统的腐烂并并不是发生在最开始。

很多后端研发在接手新的系统之后,往往对其设计的理解其实是不够深入的,来了需求之后就是一顿“兵来将挡水来土掩”,可以说是一种战术性编程,或者说的难听些“应付式编程”。

这种编程的特点有下面这几种:

  1. 快。这类程序员为了快速解决产品需求,总是以腐化系统为代价去解决问题。经过他们之手维护的系统可拓展性差。
  2. 高产。这类程序员代码量极大,可能不择手段,完全不会考虑复用,很多时候解决问题就是cv大法。
  3. 坑。他们往往只是专注于功能堆砌却忽略设计原则和设计规范,有时候命名规范甚至都懒得遵循,成本放到未来,后人买单。咱们经常提到的倒霉的小猫就是经常买单的那位。

上述共同特点就是缺乏设计,完全聚焦于快速交付,注重短期价值不考虑未来发展。

那么为什么会这样的呢?可能会受到以下三点的影响:

  1. 研发人员本身的水平以及认知还有责任心。研发人员本身认知不够,意识不到系统其实是需要考虑拓展性的,这种往往也是没有办法的,另外一种是研发人员抱有侥幸心理,虽然意识到拓展性的问题以及设计问题,但是比较懒,本着“多一事不如少一事,反正我只是过客”的心态去做系统。这类往往在外包系统中体现更为放大。

  2. 互联网背景下,老板为了快速适应市场,会进行大量业务试错,这就会要求程序员快速开发。很多程序员想要好好设计一下系统,可是无奈妥协于项目经理的一而再再而三的问你上线时间。这种情况下,设计可能就成了一种奢侈。

  3. 考评体系不合理。老猫有个朋友,之前一天他和我们吐槽,他们目前领导需要拉出他们每天写代码的量去看看他们每天干了多少活。这种真的是滑天下之大稽,在这样的考评体系下面,程序员还会好好写代码么。当然这种往往是发生在领导屁都不懂研发的公司。这类领导也是老猫最最鄙视的。技术上明明屁都不懂,还要装x去指指点点。

在这里插入图片描述

“屎山”应对之道

上面聊了这么多,我们也大概知道了为什么我们的系统会逐渐沦为“屎山”,可能是在软件发展过程中的必然,其中也掺杂着各种人为因素以及非人为因素。
当然事情还是要去解决的。那么我们应当如何应对呢?

  • 寻找合适的架构

    当咱们接到一个复杂系统的时候,其实首先需要理清楚相关的架构,知道系统是如何进行模块拆分的,另外它们的协作关系和通信方式。具体操作,大家可以访问老猫之前写的系统梳理大法

  • 遵循设计原则
    组件层面,咱们的设计原则需要遵循复用/发布等同原则,共同闭包原则,共同复用原则,无依赖环原则,稳定依赖原则和稳定抽象原则。
    代码层面,可以参考老猫之前梳理的开发中需要遵循的设计原则。

  • 避免破窗效应

    这里的“破窗效应”其实是出自David Thomas Andrew Hunt的著作《程序员修炼之道》,一扇破窗,只要一段时间不去修理,建筑中的居民就会潜移默化地产生一种被遗弃的感觉————当权者不关心这幢建筑。然后其他窗户也开始损坏,居民开始乱丢废物,墙上开始乱涂鸦,建筑开始出现严重结构性的损坏。

    聊到咱们软件系统侧其实也是一样的,在系统发展的过程中,只有在我们修复历史遗留的问题时,才是真正对其进行了维护。如果我们使用一些极端的手段保持古老陈腐的代码继续工作的时候,这其实是一种苟且。例如为了临时解决问题写hotfix接口等等。

    在我们开发的过程中,一旦系统有了设计缺陷,咱们其实应该及时优化,否则会形成不好的示范,更多的后来者会倾向于做出类似设计,从而加速系统腐化。

总结

上述就是老猫对系统沦为“屎山”的一些看法,另外的,希望大家比较再提“防御性编码”这类概念。这种思想就不应该是一个合格程序员提出的。老猫对这类还是比较抵触的。“难不成螺丝钉以为自己螺纹角度独特就不会被取代了?”,咱们把自己负责的东西尽量做到完美,是金子总能发光的,小伙伴们,你们觉得呢?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/549038.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HTML5语义化元素

在HTML5之前,网站的分布层级有哪些呢? nav,header,main,footer 这样做有一个弊端 我们往往过多的使用div,通过ID或class来区分元素 对于浏览器来说这些元素不够语义化 对于我来说搜索引擎来说,不…

CentOS 7 编译安装 Nginx

CentOS 7 编译安装 Nginx 背景下载 Nginx 源码包安装依赖包编译添加环境变量添加守护查考文献 背景 一开始使用 docker 搭建了一个 web 服务器,但是由于 docker 不太方便的部署 TLS 证书,故使用 Nginx 做反向代理,实现 https 连接。 下载 N…

【Chapter1】操作系统概述,计算机操作系统教程,第四版,左万利,王英

文章目录 一、操作系统的基本概念1.1操作系统的层次结构1.2操作系统的运行视图1.3操作系统的概念(定义)1.4操作系统的功能和目标1.4.1操作系统的功能和目标——作为系统资源的管理者1.4.2操作系统的功能和目标——向上层提供方便易用的服务1.4.2.1GUI:图形化用户接口…

Excel之数据透视表

数据透视:逻辑理解与制作步骤 一、创建数据透视表 1、创建数据透视表:每列必须有表头 (1)选择要创建数据透视表的数据------插入----选择数据透视表 (2)选择现有工作表然后点击目标表选择合适的位置插入…

FRM模型十八:Merton模型

文章目录 莫顿模型介绍(Merton)假设表达式信用利差及违约距离 代码实现 莫顿模型介绍(Merton) 莫顿模型是评估信用风险的一大重要理论。莫顿模型认为,债券是否违约这一行为归根到底是一种选择。当违约的好处>不违约…

Spring-3

目录 Spring AOP和AspectJ AOP 在Spring AOP 中,关注点和横切关注的区别 Spring 框架中的单例 Bean 是线程安全的吗 Spring 是怎么解决循环依赖的? 事务隔离级别 事务的传播级别 Spring 事务实现方式 Spring框架的事务管理有哪些优点 事务注解的…

GitLab/Github从头开始配置秘钥

1、下载git安装包 CNPM Binaries Mirrorhttps://registry.npmmirror.com/binary.html?pathgit-for-windows/ 拉到页面最底部选择 点进文件夹下载32位或者64位的版本,我的是64位就选择64的版本进行安装 2、傻瓜式安装 3、在相应的文件夹右键选择 UserName为你的用…

SSA优化最近邻分类预测(matlab代码)

SSA-最近邻分类预测matlab代码 麻雀搜索算法(Sparrow Search Algorithm, SSA)是一种新型的群智能优化算法,在2020年提出,主要是受麻雀的觅食行为和反捕食行为的启发。 数据为Excel分类数据集数据。 数据集划分为训练集、验证集、测试集,比例为8&#…

RVA和FOA转换---三

文章目录 修改初始值RVA和FOA转换RVAFOARVA和FOA的关系 本次内容包含如何修改程序中的初始值,和如何转换内存和文件的地址。 修改初始值 问题: 我们写了一个程序,可以输出一个结果,那么我们可以通过修改PE文件来改变这个输出结果…

group by和min、max函数一起使用

原始数据 查询每科的最高分数 -- 查询每科最高分数 select stuId,classId,stuName,max(score) from student_score group by classId 错误的结果 这个显然不是对的,或者说不是我们想要的结果, 我们想要的结果是 原因是什么呢?我们知道使用…

Vue3学习日记 Day1

一、简介 1、简介 Vue3是新的默认版本,拥有更快的速度,更好的语法 二、使用create-vue搭建Vue3项目 1、创建项目 1、介绍 create-vue是Vue官方新的脚手架工具,底层切换为了vite,为开发提供极速响应 2、使用 2.1、确定环境条件 2…

局部路径规划算法 - 人工势场法

人工势场法 参考: (1)人工势场法 (2)人工势场法路径规划算法(APF) 1 算法概述 1.1 算法简介 1986 年 Khatib首先提出人工势场法,并将其应用在机器人避障领域,而现代汽…