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

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

博客: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接口等等。

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

总结

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Springboot-软件授权License

无意中看到了一个简单方便的授权方式,只需几步就可集成到boot项目中。 先上地址:smart-license: 保护个人与企业的软件作品权益,降低盗版造成的损失。PS:因个人精力有限,不再提供该项目的咨询答疑服务。 Smart-licen…

Smart Light Random Memory Sprays Retinex 传统图像增强 SLRMSR

文章目录 前言1、Smart Light Random Memory Sprays Retinex概况2、Smart Light Random Memory Sprays Retinex的实现2.1、SLRMSR算法的伪代码2.2、初始化记忆喷雾(CreateInitialMemorySpray)2.3、更新记忆喷雾 (UpdateMemorySpray)2.4、计算颜色校正因子…

二十几岁的我们:在旷野中找寻自我

二十几岁,这是一个充满变数、充满机遇和挑战的年纪。它如同一片辽阔的旷野,每个人都在其中寻找自己的方向,摸索着自己的道路。这是一个既令人兴奋又令人迷茫的年纪,我们穿着不同的鞋子,注定要走不同的路。 在这个年纪里…

onnx 格式模型可视化工具

onnx 格式模型可视化工具 0. 引言1. 可视化工具2. 安装 Netron: Viewer for ONNX models 0. 引言 ONNX 是一种开放格式,用于表示机器学习模型。ONNX 定义了一组通用运算符(机器学习和深度学习模型的构建基块)和通用文件格式,使 A…

Unity引擎是否被过度吹嘘?

提到Unity,人们基本上持有以下几种观点: A. 很多人十分欣赏Unity在跨平台兼容性和大规模开放世界场景方面的出色表现。其渲染、环境特效以及AI系统为设计多样化沙盒游戏提供了强大支持。这使得Unity非常适合开发具有多种游戏玩法和互动系统的作品。 B. 一…

Java有哪些常用的集合?

1、典型回答 在 Java 中,常用的集合有以下几个: 列表(List):有序集合,可以包含重复元素。常见实现类有 ArrayList、LinkedList、 Vector 等集合(Set):无序集合,不允许包含重复元素。常见实现类有 HashSet、…

【复现】【免费】基于多时间尺度滚动优化的多能源微网双层调度模型

目录 主要内容 部分代码 结果一览 1.原文结果 2.程序运行结果 下载链接 主要内容 该模型参考《Collaborative Autonomous Optimization of Interconnected Multi-Energy Systems with Two-Stage Transactive Control Framework》,主要解决的是一个…

深入了解JVM底层原理

一、JVM内存结构 1、方法区:存储编译后的类、常量等(.class字节码文件) 2、堆内存:存储对象 3、程序计数器:存储当前执行的指令地址(计算机处理器(CPU)正在执行的下一条指令在内存…

win修改图标自定义QQ桌面图标

当安装了TIM后,想把图标改成QQ 图标见顶部,或通过网盘下载 提取码:9Ayc 操作步骤: 1.桌面右键图标,点击属性 2.选择快捷方式-更改图标 3.浏览选择下载的ico图标即可

Python中的迭代器与生成器提高性能的秘密武器【第143篇—迭代器与生成器】

👽发现宝藏 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 Python中的迭代器与生成器:提高性能的秘密武器 在Python编程中,迭代…

17双体系Java学习之数组的长度

数组的长度 //获取数组长度 arrays.lengthfor (int i 0; i <nums.length; i) {sum sum nums[i];}System.out.println("总和为&#xff1b;"sum);

心灵治愈交流平台|基于springboot框架+ Mysql+Java+B/S结构的心灵治愈交流平台设计与实现(可运行源码+数据库+设计文档)

推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 目录 前台功能效果图 管理员功能登录前台功能效果图 用户功能模块 心理咨询师功能 系统功能设计 数据库…

Linux下使用ntpdate进行时间同步

1.简介 ntpdate是Linux下用于从NTP服务器同步时间的命令行工具。 2.安装 大多数Linux发行版已预装ntpdate。未安装的可使用以下命令&#xff1a; # Ubuntu/Debian sudo apt-get install ntpdate # CentOS/Fedora/RHEL sudo yum install ntpdate 3.手工同步网络时间 执行以下命…

操作系统原理与实验——实验七固定分区的分配与回收

实验指南 运行环境&#xff1a; Dev c 算法思想&#xff1a; 本实验是模拟存储管理方式中的固定分区分配与回收算法&#xff0c;系统在作业装入前预分将整个用户区划分为若干个大小确定的分区&#xff0c;然后根据待装入作业的名称和大小到分区列表中查找满足要求的空闲分区&am…

鸿蒙Next-TextInput制作简易登录页面

Entry Component struct EventCase {State username: string State password: string build() {Row() {Column({ space: 30 }) {TextInput({ placeholder: 请输入用户名, text: $$this.username }).height(40)TextInput({ placeholder: 请输入密码, text: $$this.password })…

【网络原理】TCP协议详细解析

文章目录 &#x1f332;TCP协议的概念&#x1f338;TCP协议段格式&#x1f338;TCP的特性 &#x1f333;TCP原理详解&#x1f338;确认应答机制&#xff08;安全机制&#xff09;&#x1f338;超时重传机制&#xff08;安全机制&#xff09;&#x1f338;连接管理&#xff08;安…

电脑缺少dll文件一键修复的方法,如何快速修复dll文件

如果你遇到了电脑缺少dll文件&#xff0c;那么也不要慌&#xff0c;要解决也是比较简单的&#xff0c;下面我们一起来了解一下电脑缺少dll文件一键修复的方法&#xff0c;教教大家快速修复。 一.什么是dll文件 DLL 文件全称为“Dynamic Link Library”文件&#xff0c;翻译为中…

美国科技行业今年裁员超 5 万人;宁德时代一年净赚超 440 亿丨 RTE 开发者日报 Vol.167

开发者朋友们大家好&#xff1a; 这里是 「RTE 开发者日报」 &#xff0c;每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE&#xff08;Real Time Engagement&#xff09; 领域内「有话题的新闻」、「有态度的观点」、「有意思的数据」、「有思考的文章」、「…

Docker部署Django项目——基础

1.服务器配置 1.1centos7 系统的安装 centos-7-isos-x86_64安装包下载) VMware安装自定义配置 选择对应的系统镜像 一般选择内核4核、内存8g、硬盘80g 相关配置 1.2.网络配置 1.2.1查看win电脑虚拟机VMnet8的ip 使用ipconfig查看虚拟机的ip 1.2.2配置虚拟机VMnet8的ip…

膨胀 卷积

1.作用 Dilated convolution、Atrous convolution 增大感受野保持原输入大小 2.膨胀因子 描述的是相邻元素之间的距离 r 2 3.gridding effect 不合理的多个膨胀卷积之前&#xff0c;设计的膨胀因子不合理导致&#xff0c;在增大感受野的同时丢失了细节信息。 丢失&…