资讯专栏INFORMATION COLUMN

三年前旧代码的重构、总结与反思

Shihira / 1908人阅读

摘要:最近在维护一个三年前的旧代码,用的是框架。单元测试和语言并发控制实际上是个蛋疼的问题,夸张一点说,当时的并不能特别轻松地实现并发,甚至不能实现并发。语言的功能之一就是自带单元测试。用语言之前,我的习惯是不写单元测试。

最近在维护一个三年前的旧代码,用的是laravel框架。

从某些方面来讲,这个代码算是比较标准为了实现“在规定的时间内完成相关功能”,同时“程序员水平不高”、“经过大量优化”之后,变地特别烂的。但是其中,程序员的水平和态度是最主要的,其他相对于而言都是次要的。

当然,我就是那几个程序员之一,所以我可以放心大胆地说自己的坏话

另外本文会多次提到语言间的对比,当然本文的目的并不在此。

框架之争

无论是当年来看还是现在来看,Laravel框架思想还是结构,都算得上是“Modern PHP”的典范。

Laravel之于php,就相当于springboot之于java。

Laravel针对http请求引入了中间件,稍微配置一下便可以很方便地使用类似servlet的拦截器,功能还远比servlet强大。

针对ORM类的需求,自创了eloquent框架,使用的简洁性上也算得上是一流。

至于安装、配置、部署、依赖等等,laravel也都提供了完全通用的方案,这就很可怕了。

可以这么说,如果我们完全按照laravel的架构,完全遵照laravel的文档,写出来的代码即便不会很优雅,但是也绝对不会特别坑。

当然,laravel缺点也是很明显的。最重要的一个缺点,性能。

作为PHP框架,laravel的性能无意识特别拖后腿的地方。我们这里已经没有详细数据,但是大概的数据我们可以提供一下:一个最简单的路由,里面只有Redis::set这一个操作,并且没有任何中间件或者计算逻辑,24C64G的机器,只能支撑到大约300+QPS,即便开了opcache等也没有质的提升。至于php7,第一个7.0版本是发布在15年12月,项目上线四个月后,不要说还要等laravel支持PHP7,更不要说php7也满足不了性能需求。

这也就是我们第一版代码就已经是两种语言异构的原因。

PHP部分用于处理正常业务请求,Go部分用于处理心跳等其他请求。

技术和能力

在后续的几年内我们也在反思这个问题。

如果我们当初做的是采用tcp协议进行传输数据,服务端也用Go,那么我们还可以做很多“看上去很酷”的事情,例如:我们可以实现并到处宣扬C10K、C100K,可以到处宣扬实现了十万百万MPS(Message per second),可以将所有的任务结果流式传输到服务端,可以做到同步返回结果,可以让用户体验上更好。

但是如果这样做的话,无状态、流量、日志存储等便是要考虑的新问题。这可能会把我们培养成技术流,但是也可能把我们的项目变成新的“技术瘤”。

现在回想起当年,更重要的问题在于,我们确定当时参与者没有人敢提出这种方案,更没有人能驾驭住这个方案

维护成本

维护成本无疑是后期最大的成本。

早期我们依赖supervisord,在最早期我们经历过supervisord和docker的supervisord冲突的故障,后期我们也经历过其他项目也依赖supervisord、因为配置原因导致其他项目被停止的故障。故障么,自己的锅自己背,也没什么好说的。

但是其他的维护成本是比较多的。

你所能想到的,例如agent的保活,算是比较常见的问题,几十上百个agent总会有一两个出问题,这个我们也都习以为常了,甚至自己做一做自动修复也能解决问题。

你所不能想到的,有些人将环境相关的任务也给算到你的头上。有些人会因为“你这个系统怎么在这个环境出现了这个问题”,查了半天,对方端口没有打开。

这点在我们中间件相关的项目比较常见。通常他们会上来就问这个中间件怎么出这种问题了,实际上呢,让他们把堆栈完整地发出来之后,告诉他们“caused by里写明了,unknown host,就是你的XXX域名没配嘛”。每天都有四五个人问这种问题,也会给答疑方带来很大的压力。

在我们的其他工作中,也在不断地探索如何减少教育成本,FAQ、培训似乎收效都不高。

假装喷人似乎有效,例如“你XX的是不是开远程调试了(此处请脑补意大利炮)”,但是这种操作也不能每天都能做的。

如何降低教育成本、让开发者自己拥有自己解决问题的能力,一直是我们工作的重点。但是目前看起来,我们在这方面收效甚微。

需求、功能、BUG和变迁

这个项目我们也算是顶住了很大压力,没有接新的需求,也没有再去增加新的功能。

我们当时的理由是“他仅仅是个任务的服务端”,“你只要如此这般写这个任务便可以实现这个功能了”。

但是后续系统变迁是我们当时所没有考虑的。

后续我们有了一个新的任务管理界面,有了新的统一登录接口,CMDB的接口也几经变化。

最终这个系统只剩下API每天任劳任怨地工作着。

按理来说没人访问的接口和界面就应该直接下掉。可是至少这也是亲生的bug,我也心软,没办法下手。

根据其他系统的经验,有时候我们是不得不添加部分功能的,而这部分功能我们可能会引入很多问题。

举个例子,某些人吐槽为什么大公司的代码如此之烂,一个项目中httpclient就有四个版本。

这件事情可以理解,例如早期可能只用了HttpURLConnection进行get请求,中期为了支撑post请求,支持参数,支持超时,分别对HTTPClient了封装,后期因为引入JWTs,又封装了一次。代码冗余度变高,但是既然“系统跑得很好”,也就“没有精简的必要”。

可以理解,但是不代表可以接受。

代码冗余一直是内部项目重构时常见的问题,通常表现为为了不影响原有代码的执行,把现有的代码拷贝一份,换个名称,修改一下交给新接口来调用。

Java等静态语言合并冗余代码比较简单,编译成功即可保证大部分功能可用。但是php等动态语言我们则不敢这么做。PHP做不到“编译成功便保证基本没问题”。

就这个例子来看,一方面是开发对HttpClient的认知不足,另一方面则是开发对代码的抽象能力不够,也未留下适当的接口满足未来的需求,才会出现“一个项目中httpclient就有四个版本”的噩梦。

有些内部系统也会和早期的我们一样,首先为了做出成果,然后才是追求更高层次。

但是这并不是一个做技术的人应该有的态度。

优秀程序员的价值,不在于其所掌握的几招屠龙之术,而是在细节中见真著。

如果我们可以一次把事情做对,并且做好,在允许的范围内尽可能追求卓越,为什么不去做呢?

成果是要有的,但是一个做技术的人,应该有对职业的自我尊重、对自我价值的追求和对卓越的理解和渴求。

完美有多远?不知道,但是我以后肯定会多走几步。

单元测试和语言

并发控制实际上是个蛋疼的问题,夸张一点说,当时的PHP并不能特别轻松地实现并发,甚至不能实现并发。我们目前的服务端实际上只是做了任务转发,采用了一些取巧的方法实现并发(curl_multi),但是我们并不能实现并发控制等功能。至于说多线程(pthreads)和多进程(pcntl)的方案,实测下来也并不稳定,测试阶段便会产生coredump。

并且经过多次调优,我们也最终解决了curl_multi的性能问题,可以达到成千上万的并发,并且性能还算可以。

现在复盘一下,如果用的是Go的话,可以很轻松地用5-6行代码增加并发控制。Go语言自身性能不错,并发也很好。

Go语言的功能之一就是自带单元测试。这点和maven差不多,但是Go是少数几个语言层提供测试工具链的语言之一。

相比于动态语言,静态语言的优势之一便是安全。

可以稍微夸张点讲,静态语言一旦编译成功,除非有RuntimeExcetion,不然基本不会出问题。

而PHP这种动态类型的语言,就比较蛋疼了:不仅写的时候可能会有问题,很多IDE也无法意识到你到底是不是写了个bug,甚至过几年回来阅读代码,即便是自己参与过的项目,读起来代码也很蛋疼。PHP也意识到了这一点,从PHP7引入了类型声明,也能缓解这个问题。

用Go语言之前,我的习惯是不写单元测试。用了Go语言之后,我开始养成对所有函数都写单元测试的习惯。

我们本文中提到了很多次Go语言,实际上语言对项目的影响并不大,真正起主导作用的,还是人。

规范

运维规范对本项目的影响并不大,主要是开发规范。

后续的工作中,我不止一次·告诫业务开发,我们目前所有的规范,无论是运维规范、数据库开发规范,或者任何代码开发规范,都是我们一次一次地踩坑铺出来的路。

如果当初我们有数据库开发规范的话,表结构也不会这么坑。就像laravel框架一样,我们按照规范来写,不至于让代码上升一侧层次,但是也不至于让代码烂出水平。

在此我们强烈对小公司和开发人员推荐《阿里巴巴Java开发手册》,不仅有开发规范、还有表结构规范,无论是对开发或是对公司都有好处。

语言对项目的影响并不大,真正起主导作用的,还是人。

如果人的平均素质并不能达到优秀的话,那么完善的流程和规范将能很大程度上影响一个项目的质量。

总结

教育成本是后期维护的主要成本之一,我们也一直尝试赋予开发者自己解决问题的能力,虽然很难。

人的素质无疑能直接决定一个项目的质量。

当然,对于普通公司、新人这种平均素质达不到优秀的情况,完善的流程和规范将很大程度上保障一个项目的质量。

静态语言、单元测试等手段是保障项目稳健性的重要方式。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/29336.html

相关文章

  • 猫叔产品读记 | 爆款车厘子、重做消费品、旅游大数据(1期)

    摘要:爆款车厘子的自由卖相好看色泽鲜艳的车厘子正是春节期间讨喜的送礼热门货之一。智利农业部乐观预计,本次出口季销售成绩有望实现的增长,而中国人的春节消费成了智利果农今年最大的期盼。 本博客 猫叔的博客,转载请申明出 前言 本系列主要是猫叔对互联网相关产品(兼任产品经理)路上的视野扩展与信息记录,每日三篇,记录分享。 爆款车厘子的自由 卖相好看、色泽鲜艳的车厘子正是春节期间讨喜的送礼热门货之一...

    legendmohe 评论0 收藏0
  • 新手自学前端一些建议总结

    摘要:作为文科专业毕业,无名校背景,无任何关系,没有接受过任何专业培训,完全靠自学和看书看视频做了五年前端,在外包公司,创业公司,级公司再到新型互联网公司都呆过,想结合自己的经历谈谈对于自学前端的一些看法学习大纲关于技术栈学习,网上总会有各种讨论 作为文科专业毕业,无名校背景,无任何关系,没有接受过任何专业培训,完全靠自学和看书看视频做了五年前端,在外包公司,创业公司,BAT级公司再到新型互...

    xfee 评论0 收藏0
  • 如何维护老代码

    摘要:我们在平时的工作中,总是会遇到老旧的系统以及老旧陈的代码。弊端就是需要维护两套代码,理解两套技术选型。那么问题就来了新的代码如何和旧的代码解耦新代码我们当然是用新仓库,新选择,新打包工具。。。 我们在平时的工作中,总是会遇到老旧的系统以及老旧陈的代码。他们是业务长年累月的积累,以及因为是三、四年前的技术选型造成的系统架构的不合理以及繁琐的代码。维护这些代码总是很头疼,程序员遇到这样的代...

    lcodecorex 评论0 收藏0
  • 如何维护老代码

    摘要:我们在平时的工作中,总是会遇到老旧的系统以及老旧陈的代码。弊端就是需要维护两套代码,理解两套技术选型。那么问题就来了新的代码如何和旧的代码解耦新代码我们当然是用新仓库,新选择,新打包工具。。。 我们在平时的工作中,总是会遇到老旧的系统以及老旧陈的代码。他们是业务长年累月的积累,以及因为是三、四年前的技术选型造成的系统架构的不合理以及繁琐的代码。维护这些代码总是很头疼,程序员遇到这样的代...

    y1chuan 评论0 收藏0

发表评论

0条评论

Shihira

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<