当前位置: 首页 > 科技观察

实战-30000多行代码的核心系统重构之旅

时间:2023-03-14 23:53:40 科技观察

经典《重构》这本书里有一段话:一开始,我做的所有重构都在细枝末节。随着代码趋于清晰,我发现我可以看到一些我以前不理解的设计方面,如果不重构,我是达不到这个水平的。重构真的是一件让程序员兴奋的事情。今年年初,我们团队完成了一个复杂项目的重构,该项目是广告系统的核心引擎部分,大约有300个文件,3万行代码。从技术方案设计到最终全面上线,仅用了一个月左右的时间,没有出现任何意外。这应该是我8年编程生涯中经历过的最大最成功的重构项目了:速度够快,计划比较周密,质量过关。01先说说这个制度的历史包袱。在这次重构之前,我们的广告引擎经历了大约一年半的迭代。初期针对搜索场景,业务单一,流程清晰。2019年开始,公司广告业务开始快速扩张,收入几乎呈指数级增长。在这个过程中,我们的广告引擎面临两个挑战:1、业务场景变得更加复杂。除了搜索广告,它还需要支持信息流推荐和类似的推荐场景。2、广告流量开始快速增长。除了满足功能需求外,还需要兼顾良好的性能。经过梳理,整个引擎的大部分逻辑都可以共享,所以我们定义了一个主框架,将可扩展的部分抽象出来。这样每个场景都可以根据自身业务的特殊性实现一些公共接口。另外,从性能的角度来说,我们牺牲了一些代码的可读性,并行化了一些逻辑。随着业务的发展,搜索场景开始进入快速迭代期,新策略的加入也越来越多。这个时候,我们的主框架就逐渐变得僵化了。如果移动了主框架,搜索以外的场景也需要相应地重建。在业务快速发展时期,建设期根本不允许,只能在现有框架上进行补丁式开发。这样一来,有两个明显的问题:1.为了兼容查找的特殊逻辑,我们需要在其他场景中加入各种if判断来绕过这些逻辑。2、广告策略越来越多,积累了几十种。当框架失去了清晰的结构,一些策略的实现开始定制化,缺乏层次划分和可插拔的抽象设计。在此背景下,随着变更的积累,代码开始偏离设计初衷,技术债也越来越重。但是,我们仍然找不到合适的时机进行重构。转折点出现在2019年底,由于广告业务的特殊性,流量开始自然下滑。另外,产品运营团队专注于第二年的工作计划,这给了我们一个很好的窗口来启动这次重构。我们把建设周期定为一个月,最后只比预计晚了一天就上线了。虽然出现了两次线上问题,但在灰度期间及时发现并修复,没有造成线上事故。总的来说,这是一个难度较大但比较成功的重构项目。下面我详细说一下我从这个项目中学到的宝贵经验。02重构前做了哪些准备工作?这次重构的代码量非常大,3万多行,是广告系统的核心引擎部分。在开展之前,我们可以预见到以下困难:1.业务方面的阻力:广告是非常商业化的。这种重构虽然可以提升长期的研发效率,但是并不能直接提升业务收益,而且开发周期也不会太短。怎样才能得到商科学生的支持?2、技术顾虑:一旦重构造成线上事故,公司有惩罚制度。还有非常重的业务迭代穿插其中,交期谁也不能保证,质量也很难把控。针对这两方面的关切,我认为下面的工作起到了关键作用。让大家看到上面提到的痛点:随着业务的迭代,我们广告引擎的主框架已经变得模糊,其他几十种广告策略分散在不同的业务场景中,配置杂乱无章。针对这两个痛点,我们提前一个月开始梳理现有业务,阅读旧代码,同时阅读之前的需求文档。最后,我们将不同场景的核心流程和广告投放策略归类到一张清晰的表格中。正是这张表,让技术和产品第一次清晰的看到了我们引擎的全貌,认识到了业务的复杂性和当前技术的瓶颈。在明确了重构的目标和价值,让大家感受到了痛点之后,我们规划了本次重构的两个核心目标:1.主框架的重构:模块化主流程,重新定义上下层协议,确保界面清晰;每一层的内部也需要很好的抽象,具有很好的可扩展性。2、策略灵活可配置:广告策略根据业务意图分类抽象,策略执行条件动态可配置,策略可任意插拔。此外,我们还详细说明了这两个核心目标完成后可以带来的预期收益:1.技术收益:代码结构更清晰,更易于理解和维护;扩展性增强,引擎的开发效率将进一步提高。2、业务收益:该策略可以实现更细粒度的配置和扩展,对业务支持更友好;研发效率提升后,可以进一步加快业务的迭代速度。将重建的价值同步给大家后,进一步增加了大家的兴奋度,大家参与的动力也更强了。整体节奏的把控整体节奏的把控也是很重要的一环,让大家对这件事情有个时间期待。首先,我们将建设周期定为一个月。一方面,我们考虑了业务方可以接受的最大周期,也希望在技术上能够快速解决;同时,预留1-2周的缓冲时间,以备不时之需。另外,我们和业务方达成了协议:在重构期间,不接受引擎部分的非紧急需求,这样可以最大程度地减少并行开发和代码冲突,让团队更加专注。03实施过程中有哪些经验可以分享?这个重构可以这么顺利的实现。有4点我觉得很有价值的经验分享给大家。1.高质量的技术设计方案这是源于日常需求。对于开发周期超过3天的项目,我们都会设计技术方案,本次重构当然也不例外。框架的整体架构、模块间的协议设计、策略的可扩展性设计是本次技术方案的重点,团队讨论了不下3次。大方案敲定后,团队进一步细化了数据库、接口字段、缓存结构、日志埋点等公共部分。因为涉及到多人协同开发,所以团队约定使用文档作为沟通的接口,文档与代码始终保持同步。.在如此高的要求下,团队制作了5000多字、共36页的技术方案文档,为全面质量保证打下了良好的基础。2.预重构框架代码的PR非常关键,是我们技术方案到代码最重要的一步。我们已经梳理了重构后的包结构、模块划分、层与层之间的API定义、不同广告策略的抽象,暂且忽略实现细节。这样,主要代码就基本成型了,可以清晰的描述出我们理想中的框架。然后,我们组织了几次集中的codereview,最终形成了统一的意见。这一步可以很好的避免过早陷入实现细节,导致对主体框架关注不够,代码不稳定,后期返工反而会拖累效率。3.频繁的沟通和结对的codereview机制进入详细实现阶段后,了解现有逻辑非常重要。经过一年半的迭代,引擎代码被历史上很多人开发过,但这次只有3位同学参与了重构。整个过程中,遇到任何代码逻辑不清晰的地方,我们都要反复沟通验证,不要主观猜测,这个谨慎其实是关键。另外,在codereview方面,我们分配了对这个业务比较熟悉的同学分模块负责,两两配对,机制灵活。4、没有重构有效的测试计划,测试先行。这个原理在《重构》一书中是有强调的,也是我们技术方案讨论的重点。我这里拿出来详细展开。首先,我们前期约定:旧代码不动,完全构建新包进行重构。这样可以方便对比重建前后的结果,同时进行在线灰度实验。在测试计划中,有以下四点值得借鉴:1.端到端测试:本次重构不涉及功能调整,所以外层API的行为不会改变,所以端到端测试方式是最有效的,这是研发和QA测试最重要的手段。2.冒烟测试:QA同学提供冒烟案例,R&D同学进行冒烟测试。所有烟雾情况必须在测试前通过。这在大多数互联网公司中并不常见,但是对于大型项目绝对有效。3.沙盒环境双进程验证:上面说到,我们重构前后的代码都保留了,所以可以通过脚本抓取线上环境的输入参数作为case,然后返回的字段API会以自动方式逐一进行比较。4.在线环境灰度实验:灰度对重建非常重要。我们利用现有的ABTest平台逐步释放灰度流量,从5%,到10%,到30%,最后到100%,制定了非常谨慎的扩容节奏,然后通过日志和业务指标进行验证监控。写在最后回顾整个重构过程,归纳为以下7个关键点:1.把握重构时机2.前期梳理很重要,先找到痛点3.明确目标和价值观让大家兴奋4.不适合长期作战,与业务不平行5.需要高质量的技术方案6.重构没做过,测试先行如果大家靠谱,重构已经成功了一半。本文转载自微信?《IT人进阶职场》,可通过以下二维码关注。转载本文请联系ITProfessionalWorkplaceAdvancement?。