谈程序的腐化

写代码如同打扫屋子,有句话叫一屋不扫何以扫天下。如果单个的一个模块代码都不能管好,如何成就一个完善的软件系统?

写代码如同打扫屋子,有句话叫一屋不扫何以扫天下。如果单个的一个模块代码都不能管好,如何成就一个完善的软件系统?

今天我们来说说,一个代码模块的代码是如何一步步腐化变质,到最后程序员都不愿意去维护它,然后要么重构,要么废弃换新模块的?

代码是有一定的周期的,这个没有错。为什么有的代码跑上几十年仍然好用,而现在互联网公司的很多代码,每年都要做好几次重构?一个成立2年的互联网公司,做一个支付系统,可以做了4-5代,每次重构,这样的代价有多大?如何才能让原有的代码生命周期更加长,而不增加很多的学习维护成本,开发一次使用更久呢?

大部分程序员是没有很多机会从0开始搭建一个新程序的,更多的时候是接手别人写的代码。有代码移交还好一点,往往因为各种因素,这些因素你懂的,没有产品文档,没有设计文档,没有程序说明,程序里可能连注释都没有。

然后,程序员更新换代又极其的快,互联网时代,程序员在一个公司的平均年资也就1年多,程序就又被传给下一任维护者。很大可能的情况是,最终到你手里的程序虽然有各种问题,可以实现基本的功能需求,但代码内部各种问题让程序员总有一个冲动,重构它。

今天不想说重构的问题,而是从根源角度分析,程序为什么会变成这个样子?

什么是程序的腐化?

什么是一个软件的质量?

没有一个开发者会在init commit的时候就去降低程序的质量,在白纸上作画的时候,常常会敬小慎微。而当一个油画被刷了一层又一层以后,后面的作画者就没有那么仔细了。

软件质量的一个分类标准是软件外部质量与软件内部质量的统一,外部质量是对外表现是否正常,内部质量是对后续开发有没有坑,就是我在这里说的软件有没有腐化。

内部质量标准有:可维护性,灵活性,可移植性,可重用性,可测试性,可理解性(摘录自代码大全)。不符合以上标准都可以称之为代码腐化,形象的理解就是一个苹果,从内部开始烂了,烂到原本应该负责内部代码的程序员拒绝去维护了。

实际的代码腐化的例子:

  • 代码混乱,没有代码规范

  • 不该连数据库的模块连了数据库
  • 模块间的调用混乱:例如C#代码已经使用了EntityFramework,代码中跳过EntityFramework,直接用数据库连接修改数据。
  • 框架与其他不一致,不统一:有的包管理使用gradle,有的使用maven。有的后台用.Net,有的用Node,有的用Java。用了HttpClient,又使用Feign去连接其他应用模块
  • 有一些设计前后不一致
    1. 有的代码使用了统一的错误定义CommonException,有的用原生的Exception。有的代码用了统一的ErrorMessage(见下例),有的却随意写错误信息(下面代码19行)
    2. 微服务模块,有resource层接口,定义访问的路径,resource的Impl,service的接口提供具体的数据接口,serviceImpl提供具体数据获取的实现。而在具体编码时,将大量的业务逻辑写入了resource的实现中。
    3. 定义了一个枚举Enum,但在具体使用时,却直接使用枚举值
  • 太复杂的抽象不能做方便的变更:一开始设计的Job系统,上面是2-3张图片,下面是动态生成的问题。代码层面对于此设计做了很细致的抽象。突然产品提出了某一个Job的图片有特别,要求显示10张图片,就对抽象的图片部分做了if-else的处理……
  • 无用代码,废弃的接口没有标明

代码腐化的原因

没有代码会是一开始时候就腐化的,腐化都是循序渐进的,要一个过程。我总结了一些代码腐化的原因:

  1. 没有统一标准,或者没有严格执行
  2. 多头维护
  3. 架构没有落地:没有统一标准,或者没有严格执行
  • 统一标准之代码规范

每个程序员都是有自己的审美的,例如即使是缩进长度这种代码里不影响任何功能的东西,有的喜欢空4格,有的喜欢2格。有的喜欢黑色的编程背景,有的喜欢白色的编程背景。有的喜欢if后直接跟上左括号,有的就喜欢另起一行。

代码规范还是要有的,包含各种格式定义,大小写规范,命名规范等。前端有各种lint工具(jslint,tslint)可以帮助规范,后台的ide也有一些方法帮助。像Baidu,Google这样的公司还有构建时的自开发的检查工具,所以常常一个资深程序员第一次开发的代码要花上1-2天才能提交通过。

代码规范的混乱,直接导致代码可读性的降低。可读性直接影响后续的生产力。一个程序员天天对着看不顺眼的代码,怎么可能高效?

  • 统一标准之基础规范

除了代码规范外,项目命名,通讯方式,基本的程序框架,后端Java的springboot,sprintMVC,前端的angular,vue,react等都需要统一,还有统一的基础环境(eureka,elk,redis,apigateway等)。

不统一的后果是各种部署,管理,编码的低效。例如搭一个jenkins,然后部署服务A用的Maven,服务B用的gradle,就导致编译代码写2套,如果写一套基本一样的,当然会快一些。

  • 统一规范之公司统一框架

刚才说的统一,很多是从公司层面的统一,如果大家都只用springboot,都沿用统一的后端框架,前端统一用angular。那么这个时候,为了方便统一,就需要有代码相关的脚手架工具,直接生成基本的统一项。这样一个工具的好处是可以直接一键完成许多基础工作,并完成了底层的统一工作。

我统计的java代码中可以统一的部分(包含但不限于)

Http调用格式,统一用content-type:application/json,response也统一要求这样。


RestApi的设计标准:POST请求是不是一定要求有Body,POST和PUT请求是否可以带上Query参数,GET请求是否可以带Body,批量查询接口输入变量为List是否还是要用GET请求Query传参的方式

HttpClient的标准化

框架,如SpringBoot

项目管理工具:Maven,Gradle

项目的CI,CD

配置管理模式,例如统一成一个配置文件application.properties

环境变量配置方式,qa,stag,prod。不要有的人写stage,staging,也不要写成production等等细节

代码基础结构:例如标准的maven目录的结构

Restful接口设计统一:大小写,命名方式,Body的最大大小。

例如,Post接口是否可以加PathParameter和QueryParameter。Post接口是否可以不带Body。

其他配套功能的统一性:调用链,动态配置管理,缓存,分布式事物

数据库的统一:统一数据库,数据库版本,是否可以使用存储过程等。关于数据库统一性不在这里展开,这点也非常的重要。

多头维护

代码腐化的一个很重要的因素是多头维护,甚至是多代维护。

一个公共项目,多个开发团队都在维护,那就很难统一标准。初始版本有一个架构,然后换了一个架构,开发更是换了几批。人多手杂说的就是这样的情况。

任何开发团队接手一个旧项目时,其实都是有学习和适应的成本的。频繁的变更开发人员带来的坏处就是反复的人为制造这个成本,其次就是有几率丢失之前的一部分标准和架构规划。

架构没有落地

 代码模块的功能设计规划,制定的标准,没有详细的落地。架构定了一套,开发没有严格执行。每天写代码这么忙,架构只管架构,不管细节。开发每天撸代码,只管功能,不管架构和代码质量(这个质量不是指功能实现上的质量,而是说严格执行各项统一标准的程度)。

甚至说,一个高层服务,不能调用同级服务,只能调用底层服务。因为开发的没有严格执行,甚至加了数据库连接,直接去取了数据库,这样的事一旦开了口子,就像黄河决堤,不可收拾了。

所以,以上说的是架构的落地落实很重要,让所有具体的开发参与者落实同一个标准。架构就需要落实相关的设计,相关的文档,相应的执行检查。现实的情况从来没有靠文档解决一切问题的,可它能解决80%的问题,另外就尽量减少开发人员的变动,以减小换人带来的代码腐化问题。

防止代码腐化的建议:

代码规范标准化,统一化治理

代码的内部质量其实很难保证,规范执行也更多的靠人治,甚至个别标准化的东西,只能通过代码层面去检验,无法通过测试或其他手段进行。

另外,虽然有一些通行的默认标准,更多的标准是代码的负责人自行确定的标准,完全根据喜好来,就像前面说的缩进的长度。好比是老妈和丈母娘都跑来你家里帮你打扫卫生,老妈喜欢把厨房里的锅都一路洗好挂起来,丈母娘喜欢找一个橱柜,都放在橱柜里,你能怎么办?没关系,只要确定下一套标准,不要经常改就好了。

以下是秦苍的规范范例,就简单的命名来说就有如下的规范:


[应用]


commonservice


[服务]


sms


[模块]


server


[版本]


1.0.0

GAV

maven的GAV,细到每个模块,pom.xml中使用:

groupId=com.omniprimeinc.commonservice.sms

artifactId=commonservice-sms-server

version=1.0.0

Spring

spring.application.name=commonservice-sms

spring.application.version=1.0.0

Metadata

eureka.instance.metadataMap.layerId=[层]

eureka.instance.metadataMap.groupId=[应用]

eureka.instance.metadataMap.groupName=[所在应用 api gateway 的 ${spring.application.name}]

eureka.instance.metadataMap.applicationId=[服务]

eureka.instance.metadataMap.isApiGateway=[当前服务是否 api gateway]

eureka.instance.metadataMap.owner=[owner域账号]

eureka.instance.metadataMap.owner.fullname=[owner姓名全拼]

eureka.instance.metadataMap.version=[灰度版本,默认填写1.0]

eureka.instance.metadataMap.version.application=[服务版本 ${spring.application.version}]

eureka.instance.metadataMap.version.framework=[框架版本]

eureka.instance.metadataMap.type.deploy=[部署方式: docker/ecs]

eureka.instance.metadataMap.git.url=[git地址]

eureka.instance.metadataMap.git.commitId=[git commit id,不用填写]

eureka.instance.metadataMap.admin.enabled=[是否启动admin:true/false]

eureka.instance.metadataMap.admin.notify.dingding=[通知发送-钉钉url]

eureka.instance.metadataMap.startuptime=[启动时间,不用填写,通过 bootstrap 阶段获取]

eureka.instance.metadataMap.compatible.disconf.app=[兼容: 优先选择这里的配置作为本服务在 disconf里的APP名称]

eureka.instance.metadataMap.compatible.restart.app=[兼容: 优先选择这里的配置作为本服务在 冷重启里的APP名称]

各种各样的规范:

架构严格落地

像前面说的标准,特别是自定义的标准,都需要落地。第一优先文档,第二是团队内部达成共识,第三是执行。

严格的codereview

防代码的腐化执行是一个关键点,这个关键点就是codereview。在这个时间点,再次与开发强调标准的重要性,让开发知晓执行,让测试监督,让架构严格检查。对于不符合的,开发要再次去学习执行标准。

减少开发人员的变动

如果一个团队内,原本标准就是统一的,团队内实行敏捷开发,任何一个开发都可以替代其他开发工作,那么两个人交换任务就没有问题。如果团队内都不统一,这个变动就会严重影响开发,代码腐化的可能会变的很大。

而团队间,标准统一的可能性比在团队内更难,要避免团队间的项目移交才是最优的方法。

代码模块架构Keep it Simple&Stupid,使用一眼就明白的架构

现实世界中,业务需求永远的跑在技术需求前面,很可能架构相关的设计没有文档,没有说明,一旦架构师不在,原来的开发换人了,导致原先的标准和设计无法继续下去,一旦有交接,标准的丢失和架构的变化不可避免。

这时,此条原则就能发挥作用。要使用傻瓜化的架构,就是任何一个新来的程序员,一眼就能看明白的架构。例如使用公司统一的项目代码生成器,脚手架,生成统一的代码结构,不需要程序员再投入代码结构学习的成本。而且通用架构就意味着,后续的架构师接受原先设计的可能性大增。

分层的概念很早就提出来了,为什么MVC的概念会这么受欢迎?我觉得是因为它足够傻瓜化。数据库的架构结构中,定义repository,service接口,serviceImpl的实现等也成为了很通用性的设计。

有一个挺经典的例子就是asp.net.MVC的程序组织架构

三个文件夹:Controllers,Models,Views直接约定,并强调约定大于配置。只要是开发过这类程序的程序员,都知道从Controller看路径和RestApi入口,Models看数据结构,View看Html视图。就不需要额外的学习成本。

Maven的标准目录结构也是一个例子。(见上图)

定期清理维护

如同像打扫房间一样,一段时间不打扫,自然会有边边角角的脏东西出来。那么有没有定期的去清理呢?还是不管他,每次看到地上的一团纸,都绕过,而不是去扔了?

定期的清理维护不单是一个维护代码的过程,更是一个重新梳理和统一标准的过程,让现有的开发和架构,再次的达成一致,以提高战斗力

防止代码膨胀

微服务概念的提出后,很好的解决了一个问题,我们的一个代码模块应该写多少大的问题?一个模糊的建议是一个Sprint能重写的大小,如果更大,就应该要差分。有时候代码的清理维护工作也要以这个原则来处理,不能出现过大的代码模块。

因为过大的代码模块,首先带来的是程序的复杂度,让程序员理解起来要更多成本。其次,模块内部的耦合度必然也提高了,增加了难度。这时,需要的是切分出一个新模块出来。

总结


代码腐化是程序开发的一个经典问题。代码内部质量的降低,外部质量确可以被客户接受。程序员经常想着,等业务没那么忙的时候,做点清理,做点模块局部的微重构,我的经验告诉你,这些都是假的。如果定下了定期清理,定期检查拆分,就必须立马去做。做这些事的优先级要远高于业务需求。

为什么呢?想象一个程序C++的printf,里面的代码写的非常的烂,变量命名都是p,m,k完全不能直观理解含义,我们需要郑重的去处理这个模块的代码腐化问题吗?

其实不需要,因为printf模块的代码需求,就是把输入的内容打印出来,永远都是这样,没有任何新需求,没有新需求就代表没有程序员需要深入内部去改它的代码,不需要改的代码,我们是没有动力去解决它的腐化问题的。

有必要经常深入解决腐化问题的代码,必然是业务需求很多,经常要变更的代码。不要等到它已经腐化到代码生命周期都快走到头了,才想起来去维护清理它,要经常维修一下,才能更好的让它发挥作用,是吧?一辆不打算开的老爷车,发动机坏了就坏了,一辆经常开的二手奔驰,定期维护少不了。

作者介绍

林松

十多年开发经验的全栈开发及团队管理者,从事过C++,C#,Objective C,JAVA,Node等从前台到后台的开发,也有多年的项目管理经验和团队管理经验。最擅长解决各种复杂问题。现负责秦苍科技POS产品线资深开发、架构。

时间: 2024-10-21 08:45:36

谈程序的腐化的相关文章

浅谈程序员的行业选择---程序人生

引言 本篇博文接着许久之前的一篇博文<浅谈程序猿的职业规划,看你如何决定自己的未来吧.>,继续探讨一下程序员行业相关的内容. 行业的选择不仅对于程序员来说非常重要,对任何一个人来说都是一样的.只不过对于程序员来说,行业更容易被忽略.从程序员每天热议的话题就能看出来,大部分的热议话题都是C#和Java哪个更牛B,或者IOS和Android哪个挣得钱多,很少看到程序员去讨论两个行业谁更有发展前景. 鉴于此,今天我们就来着重讨论一下程序员行业的选择,行业和语言一样,没有谁优谁劣,只是一种选择罢了.

浅谈程序员创业(要有一个自己的网站,最好的方式还是自己定位一个产品,用心把这个产品做好)

浅谈程序员创业 ——作者:邓学彬.Jiesoft 1.什么是创业? 关于“创业”二字有必要重新学习一下,找了两个相对权威定义: 创业就是创业者对自己拥有的资源或通过努力能够拥有的资源进行优化整合,从而创造出更大经济或社会价值的过程.——百度百科 创业是一种思考.推理和行为方式,它为机会所驱动,需要在方法上全盘考虑并拥有和谐的领导能力.创业必须要贡献出时间.付出努力,承担相应的财务的.精神的和社会的风险,并获得金钱的回报.个人的满足和独立自主.——互动百科 两者都给出了明确的创业定义,这里想着重想

谈程序员的出路

http://blog.chinaunix.net/uid-25838286-id-3068153.html 从我做小程序员开始,就从未间断的在论坛看到有人在问程序员的出路在哪里,其实我很能理解这些人的想法,在行业做了几年,有些感想跟大家随便聊聊.俗话说,365行,行行出状元,此话也适用于IT行业,尤其是程序员.当你迷茫找不到出路,又想快速成长和提高的时候,有两种方案可以供你参考:1.努力成为你工作环境中最优秀的人(技术最好的人):2.跳槽,去另外一家公司做比你能力要高的工作.针对工作环境的不同

从实际项目中的一个改进细节谈程序的易用性优化

说明:一下内容是笔者一个项目上真实的例子,这个系统是一个收费系统,需要打印发票 ,打印发票的时候需要将发票代码和发票号码打印在实际的发票上(税务上要求的).留存于此,希望对大家有启发. 从发票代码的改进谈程序的易用性优化 最近老有收费员领用发票的时候,输错发票代码,导致成百的发票打印作废.分析原因,最直接的原因有2: 1,收费员不仔细,没有仔细核对发票代码,领用录入时,和发票打印时. 2,可用的发票代码太相似:一个是 141001320043 ,一个是 141001420043,只有1位只差.收

转:浅谈程序员的英语学习

转自:http://www.cnblogs.com/haoyifei/p/5687235.html 浅谈程序员的英语学习 作为在中国工作的程序员,不懂得英语似乎也不妨碍找到好工作,升职加薪.但程序员这个工种则稍有不同,因为程序,尤其是高级语言,基本上都是由英语和数字表达式构成的.英语对于程序员十分重要.我的大学本科全部采用英文教学,工作时也经常会遇到外国人,和他们谈笑风生,自认为自己的英语水平比园子的平均水平高一点.下面我就根据自己的经验来说说英语的学习方法. 为什么要学习英语 学好英语你可以直

浅谈程序员该具备的自我修养

各行各业的工作者,都有其要求,那么作为程序员,我们又该具备哪些素养呢?博主在这里浅谈个人看法,如有不当之处,请大佬们指正. 一.知识储备 1.数学 或许在很多人看来,学计算机用不到什么数学,最多也就是一百以内的加减乘除,用在for循环.数组索引之类的上面.但其实不然,大部分人这样觉得是因为基本都工作在应用层,所以相对而言,用到的数学知识会比较少,也比较浅显. 而当从应用层更深地学习研究时,就需要一定的数学能力了. 2.计算机 1)操作系统 操作系统(OS)是配置在计算机硬件上的第一层软件.是对硬

周鸿祎谈程序员创业

原文链接:http://geek.csdn.net/news/detail/45409 昨天与大家分享了自己的创业经验,得到了很多程序员的鼓励与支持,诚心感谢大家的共鸣!由于极客邦SOHO项目开始不到半年,怕对各位程序员有所误导,特意找出周鸿祎在接受媒体采访时分享的程序员创业经验,大家一起努力,从一个小程序员干出自己的一番事业!正文如下: 程序员创业的特别之处 关于程序员创业,我之所以愿意谈我的观点,是因为我是程序员出身,也是从小公司做起来的,我自认为对待很多程序员个人包括共享软件作者和一些小网

从张帅进入八强来谈程序员的苦恼

今天是2016年1月25日,亲眼目睹(电视直播)了张帅进入澳网大满贯女子8强,一个丑小鸭到白天鹅的童话故事又悄无声息的上演,并且真真切切的感动着我,一个活在底层的程序员. 张帅,说实话,我现在都怀疑我是不是记错了人家的名字,不过我决定先不百度,让自己的无知继续下去.名字有可能记错了,这怪我,但重要的事,我可不会搞错,那就是,这位透着灵气的姑娘进入了8强赛,着实让我为作为一名中国人而感到自豪,这种民族自豪感不知不觉就从心底里悄悄迸发了出来,并且来得如此强烈.虽然没有完整的看完她和海森的比赛,但在有

谈程序员身边的商机

就在这两天,世界互联网大会在乌镇隆重召开,IT圈可是热闹极了.各大老在谈论互联网的同时,也谈论了属于我们的机会.特摘录了下面3句话送个大家. 习主席说:"赚不到钱,并不是没钱可赚,而是你不在赚钱的圈子内.马云说:"成不了富人,并不是不能变富,而是没有富人的思维."我们不缺机会,缺的是了解机会的意愿,判断机会的眼光,尝试机会的勇气,坚持机会的恒心以及相信自己的信念. 抢购商机 现在每次有抢购机会都有黄牛存在.以前抢购火车票的黄牛现在没了机会,但他们又出现在各大团购网站上. 前段