Fxxk PHP! Javaer迁移大型php项目心得
在迁移旧代码的时候,最难受的是莫过于,看着眼前这一大坨的代码,你盯着它,它看着你,看了一遍又一遍,但还是不知道代码是用来干嘛的,无注释,无文档,变量名毫无意义,上下文似乎各说各话,一个方法竟然超过2000行,随处可见的奇怪写法,遍地都是一个循环中if嵌套7,8层的奈落狱,要拿到某个属性竟然跨越了7个类,十多个方法,你才看到那歪歪扭扭的SQL语句上注释写着“RUN”
1. 背景
在公司项目中,作为Javaer迁移了一年多的php代码,对此真是深有体会,切实感受到当中的一些痛苦,并且形成一套组合拳应对这样的复杂局面,给大家分享一下。
2. 准备你的迁移环境
作为程序员,我相信大家其实并不只会学习一门编程语言,从c++到clojure可能有人都摸过一遍,所以大部分人认识一个新项目的习惯就是:搭建开发环境,然后把项目跑起来。
但其实,在迁移的时候,你最需要的可能只是一个php实验环境,而不一定需要真的把php项目给跑起来。我知道肯定有很多人会反对,“你不把项目跑起来,怎么和之前的项目做对比呢?”诸如此类的,且听我再多说几句。
php作为脚本语言,能不能跑起来并不重要,我们在迁移的时候最重要的是迁移当中的业务逻辑,而不是去学习之前php之前的代码写法,也不是去找bug,迁移最重要的是:理清业务逻辑,并用新语言重构出来
2.1 安装php环境
所以这里我只推荐你本地安装一个php环境即可,能够做到测试php代码的单个方法。
在windows上你可以用phpAdmin,而在mac,直接brew install php
就行。
3. 如何不那么痛苦的迁移
在迁移旧代码的时候,最难受的是莫过于,看着眼前这一大坨的代码,你盯着它,它看着你,看了一遍又一遍,但还是不知道代码是用来干嘛的,无注释, 无文档,变量名毫无意义,上下文似乎各说各话,一个方法竟然超过2000行,随处可见的奇怪写法,遍地都是一个循环中if嵌套7,8层的奈落狱,要拿到某个属性竟然跨越了7个类,十多个方法,你才看到从db查询的语句,更别说还妄想找到之前写这段代码的同事。
你的腹中甚至开始出现生理性反胃,仿佛进入到了里世界,遍地都是残肢断臂,尸体上的腐臭不停的冲击着你的鼻腔,偶然看到一只小狗在大快朵颐,看到活物的你似乎安心了一些,但是当它回过头来的时候却是长着一副人脸!!!是不是描绘的还挺形象的,而这就是我曾经深入探索某个属性的来源的真实感受。
接下来才是重点,《屎山探索指南之php》
3.1 了解php语法
你不必成为一个专业的php开发者,就像我迁移了这么多php代码,我仍然不会使用php搭建框架,我也不知道php有什么后端框架。
但是你还是需要了解php的基本语法,至少你要能做到,会新建一个test.php,并且会使用php test.php
输出你对某个方法的结果。这就像是检测仪!能让你在屎山里找到能摸能吃的东西。
比如这样:
菜鸟教程的里面,你起码也得毫无压力看到面向对象才行。
3.2 开始探索屎山
在拿到项目的时候,至少会有一两个同事会跟你说一遍哪个模块是用来干嘛的,不至于让你在屎山中迷路,这也是你的切入点。
如果同事讲的世界观(内容)是在过于庞大,记得一定要先记下来,不然有时候会因为走神而错过某个关键的信息点,导致你迷失在屎山之中。
接着,以迁移某个功能点为例,迁移时,不要着急先写代码! 这很重要,在不了解全貌就开始一比一翻译代码是很低效的。至少你要把这个功能点的代码快速过一遍,并且一些关键点做上标记。
比如这样
接着你就能在php storm中的bookmarks中找到你的所有书签,双击就能直接跳转,这真是太方便啦!
这个阶段你要能做到:
- 大概明白某个功能块代码是干嘛的
- 使用bookmarks做标记
- 直接在原有代码上写注释!甚至直接重构你看起来那些很别扭的代码
3.3 怀疑的地方写测试用例测测
有些旧的代码封装了一些工具方法,但是这些工具方法往往缺乏单元测试,而且没有文档注释说明!不过一般好在这种方法的入参都比较简单,可以自己新建一个test.php
文件,然后把这个方法复制进去试试。
亦或者是看到不明白php的某些语法,这也是一种比较好的方式。因为你肯定想不到下面的输出竟然是true
<?php
$flag = empty('');
if($flag){
echo 'true';
}else{
echo 'false';
}
?>
这个问题的具体原因可以看这篇博客:PHP中空字符串介绍0、null、empty和false之间的关系
3.4 为什么不试试AI?
chatGPT, google AI等等等,这些AI工具都是你的帮手!不过你不要把整个.php文件都丢给AI,这很有可能会导致代码泄漏!!! 这种行为和把公司代码上传到github一样恐怖。
3.5 善用思维导图,攻克php代码
某些时候我们需要构造一个对象返回出去,但是这个对象的部分属性又特别折磨人,横跨好几个模块,类,而且上下文中这个属性还被改过好几次。即便是使用上面用bookmarkers做标记都能标记个十多处地方。
这个时候我强烈推荐你使用使用思维导图,或者流程图工具帮你理清楚来龙去脉。
在线的一些工具有:https://draw.io/
3.6 代码是死的,但是需求是灵活的
即便用了上面所有的方法,你仍然还是看不懂那段上古卷轴。你要及时抛出这个问题,不要埋头苦干,可能这本就是已经废弃的功能,找PM确认。
如果废弃不了,那么找你的上级反馈,能否把这段功能以接口的形式提供出来,新的项目调用这个接口,在新项目上线后,逐渐抛弃调用接口的这种方式,重新构建这段业务代码。
4. 自测部分
经过漫长且痛苦的重构,终于你的代码写好了,但是距离代码能够上线的程度可能还有很大一段,你不知道你的代码是否有奇奇怪怪的bug,可能是新增的,也可能是因为你的重构,把bug也一起抄了过来。
以下有几种办法可以降低你的代码bug概率
4.1 unit test很重要
虽然很多人不喜欢写unit test,但是!这其实是最好的一种方式能在开发阶段就把问题发现,你的unit test覆盖率越高,那么你对自己的代码信心也就越强,测试反馈问题时处理bug的速度也就越快。
即便这段重构任务的时间紧,任务重,我还是推荐你在关键核心业务上写unit test,很有可能就发现一些隐藏问题。
因为很多测试都是黑盒测试,几乎不可能覆盖到所有的代码逻辑,而且即便他的测试流程覆盖到了,但是又因为没有察觉到,或者理解业务的时候就是错误的理解都会导致这个bug被忽略,从而上线。而上线后的排查难度相比于本地则更是“惊心动魄”,经历过处理线上bug的大伙相信明白我说的意思。
4.2 如果有自动化测试脚本则更好
自动化测试脚本,这理应是测试需要做的,通过接口的不同入参,拿到response后,直接和期望值进行比较,或者和DB的数据改动比较。
而因为现在是在做迁移,更多时候我们是在和旧的接口返回做对比,这是比较方便的。
所以如果开发的时间比较富裕,你可以自己就写个简陋的自动化测试脚本,接着你就可以使用这个脚本,快速的使用不同的参数测试,通过和旧接口返回的比较,更快的得到一些特殊场景下才会出现的bug。
5. 另外一些想说的
虽然重构确实是一个挺折磨人的任务,有些陈旧的代码看久了确实会让自己产生生理上的反胃。但是如果你使用一些好的、固定的流程去排查,做好代码的兜底,便可以远远降低上线后代码异常的风险。
比如 互相的code review, 结对编程,使用静态工具checkmark, sonaqube扫描代码漏洞等等。
又比如拼多多优惠券BUG事件那个,应该做的一个兜底就是限制单个用户在一天使用优惠卷的数量。在这种价格金额,汇率换算的时候应该特别注意,最好多找几个同事review一下代码。
同样代码上线后,并不意味着重构任务就完成了,异常状态的检测,日志的打印,系统运行时的稳定性都是才刚刚开始考验。
祝大家重构代码顺利哈哈哈