没技术改造的“古董项目”ci交付就是面子工程
文中的古董项目是指技术相对较老,历史代码时间久远,项目几经转手,项目代码已经没人敢大刀阔斧的修改,只能修修补补。
当有新逻辑要添加的时候,就从入口到底层单做一条链路,没人敢修改原来的业务链路。
“古董项目”什么样
在很多互联网企业或者说技术驱动的公司,技术变革推动起来那是顺风顺水,团队的驱动能力非常强。但是很多非技术驱动的公司,企业系统的技术栈并没有那么先进,没有微服务、没有服务网关、没有服务治理、没有APM、没有容器化、没有自动化测试。这样的系统都几乎拥有如下的特点:
- 历史代码6年以上,并且代码数量在不断的增长,而且增长速度很快
- 代码仓库里面留下很多开发工程师的辛苦劳作,如果说想按开发工程师人数纪录都不如用第几批团队更准确
- 新需求每次都从入口层新作一条逻辑到数据层,项目中已有逻辑几乎没有办法再次使用,最重要的是看不懂,理不清楚,尝试理解历史遗留代码,不如新加更快速
- 仰仗于人工集成测试发现问题,按照菜单和页面布局写的测试用例一样的不再可靠,测试工程师自身对系统的熟知程度是上线后是否稳定的基石
这类项目大部分出现在一些非互联网公司,很多项目在最开始是以项目外包的形式完成了第一个版本上线,随后的需求就是不断的积累代码量,代码的体量非常大而且都没有单元测试、没有经历过代码扫描,如果有机会可以用SonarQube类工具提供的规则扫描一遍,会发现技术债务都是以千为单位统计的。由于项目历史悠久,同时项目的主要开发人员又是外包人员,中间还有可能由于外包商问题、内部服务商资质问题、领导更迭问题导致外包服务商都换过好几波。
上面的各种问题都导致了一个结果,代码冗余,并且没有人可以修改或者重构,每次有新的需求都是从入口层重新开发一个新逻辑直接从入口层到数据持久化层,和原有逻辑不相交。这就导致系统中有各式各样的烟囱式逻辑存在,同时还有可能导致数据库中也有多套相同数据结构存在。这种做法常见的有新加入口参数,在原接口里面利用条件判断进入新逻辑从而建立一条看似相同入口其实两条逻辑链路的数据流,我在工作中就遇见过一个接口入参几百个的情况,所有参数没有注释,当我面对这个接口的时候我已经放弃了接口测试;如果加参数无法解决问题,那么就加一接口,新写一套逻辑单独处理当前新的需求,这样也会伴随着从代码逻辑到数据库全部都是新的和历史逻辑完全没有关系。
别看有这么多的问题,项目的按计划的发布还是按部就班的进行,固定的人再固定的机器上build出的发布版本也能顺利部署,但是换人、换设备,项目就会变成各种难以驯服的野马了。
古董项目和持续集成
这样的古董项目就如同一个1970年的拖拉机,修修补补还能运行,尽管很慢、很费油,但是聊胜于无。那么如果我们非要让他和当今机器化、自动化的农业种植相配合就会发现这台拖拉机就是最大的痛点。提速提不起来,降本降不下去。老旧的信息系统开发也是一样,如果我们非要让持续集成平台相结合就会发现各式各样的难以调和的问题。
各种配置无法抽离
持续集成系统都是有自己的构建节点进行代码构建的,就如同前面所说很多古董项目都是脱离开某一个固定的构建机器就无法正常交付的,当我们用流水线的构建机开始build的时候会发现各式各样的奇奇怪怪的问题,写死的路径、特别版本的依赖包、特别的依赖服务,最后好不容易磕磕绊绊的走到最后,在build出来的包根本run不起来,无法正常对外提供服务。
代码行数会让sonar忙上一整天
代码工程中存在着那么多特别处理,例如加一个函数,加一个类,在原函数中加一个逻辑分枝,因此有很多代码文件都非常大,随便打开一个纯代码文件都万行以上,代码文件都1兆以上,sonar扫码一整天都没有办法完成项目的全部代码扫描,exclusions配置了好几百个类,扫描的结果完全没有办法评价项目的实际静态质量。
先技术改造,在持续集成
上述的种种问题并不是说古董项目就不要持续集成,而是不应该越过技术改造的阶段直接让持续集成背负这么重的技术债,只做面子上好看的事情。先将项目做拆分,将一个大war包,拆分成一个war包多个jar包的形式,将公共的内容抽象出现,war包工程通过引入jar依赖完成调用。在这种改造过程中在审视是否需要微服务的改造,是否需要APM。过程中就和剥洋葱一样,最后就变成了一片一片的小工程了。那么小工程在做持续集成、持续交付,在做其他方向的技术升级就会变得更加容易顺应实际情况。
测试先行
重构之前,应该先完成对应的自动化测试,这部分即包含重构中的必备伴侣单元测试,也包含了E2E的UI自动化测试。单元测试是保证函数级别重构没有问题,而E2E的验收自动化才是真正的业务实现,只要保证重构后的业务逻辑不变,其实业务方并不是很关心你用什么技术实现,重构是研发自我救赎,不是为了业务实现。
除去自动化测试,那么人工的验收变得更为重要,发起大面积的人工测试会更有益于重构后的系统,这里面既包含了探索测试也包含了一些功能步骤类的测试用例的验收测试。这样既能找到跟多的问题也能保障和原来系统的业务逻辑、业务交互是一致的。
重构和新需求
重构并不是说不接受新需求,而是将部分功能抽离出来,做到服务层分离、数据一致,也就是将服务层像洋葱一样,一层一层的分离,但是数据层归入一处,保持一致性。新需求尽最大努力涉及到已经剥离出来的微服务上而不是继续在原有的巨型代码仓库中叠加。
这种其实和原来在代码中加入新逻辑,新参数的方式是一样的,最大的区别就是在不同的项目中开发,其实在原有系统中烟囱式的开发新功能,和独立出来新的微服务开发在业务开发上几乎没有差别,最大的区别就是对原有系统和业务的理解。