账单重构总结
前言
- 去年的年初开始接手账单业务,前期做过几个小需求,写代码的过程十分痛苦
没有模型设计、模块之间耦合严重、面条代码满屏飞....
- 想着接下来自己都要维护这一坨坨的代码,实在无法忍受,便萌生了重构的想法
但由于账单业务的了解不够深入,一直没有开始重构事项
与此同时,也在找各种关于贷后相关的资料,增加对业务的了解深度
- 但是如果按部就班,或许现在还没有将账单的前端应用重构完毕,回顾下来,主要得益于两个契机:
- 启动了「账单单机构」需求,其实更多是面向 BFF 链路的改动,跟前端应用侧关系微乎其微
- 但对业务侧而言,是一个全新的链路,而且需求面向监管合规,时间要求上又没有那么紧张,所以就借助这个需求顺水推舟...
- 主管,对业务来龙去脉都很了解的人,在项目中搞了个针对核心模块 Domain 抽象的 PR,给了我重构事项上一些设计灵感
- 最后,真正让我迈出重构那一步的,是由于当时感觉虽然看了挺多资料介绍贷后相关业务,但总体上还是「浮于表面」,这个问题主要在与后端合作时非常明显
被问及一个问题时,没法直接回答上来,得去翻代码,显得特别的「不专业」,更别提得说服自己「我是账单前端的 owner」了
- 于是本着「我得把账单业务彻底啃下来」的心态,就开始了对账单前端项目的重构工作
- 最终,历时 7 个月时间,整体重构的开发工作才算完成,过程比较坎坷,以下也不全是重构过程中的实践,部分是重构完成后复盘发现做的不够好的地方,将做的好的、做的不够的总结出来
一、重构原则
1. 什么是重构
重构是对软件内部结构的一种调整,目的是不影响软件功能的前提下,提高其可理解性,降低其修改成本 ——《重构,改善既有代码的设计》
2. 为什么重构
通常是一件低 ROI 的事情,需要投入的时间和精力与回报相比,往往是不成正比的,这一点需要再三斟酌
如果你对你的重构计划产生了动摇,可以先取消掉这个计划,直至你做好了相关准备
- 重构不是目的而是手段,在决定重构前要认真思考为什么要重构,即,想清楚价值与成本:
- 价值:
- 成本:
相关信息
从价值角度,我根据自身的实践经验给出一些量化的指标,仅供参考:
- 业务:按照目标架构重构后,是否能够支撑业务 1 到 2 年的快速迭代?如果不能,请重新审视这次重构的价值
- 团队:重构后是否更有利于团队协作?
- 相比之前,是否能改更少代码实现相同功能
- 即使参与者对业务不熟,也不容易出 bug
- 是否有足够的有效注释来帮助参与者理解项目代码
- 项目:更稳定、干净的依赖
- 个人:作为 owner,是项目的第一责任人,通过重构给业务、团队带来增益的同时,一定程度上也在给自己减负(时间、精力的投入,心智负担)
重要
从「个人」角度,我更多的考量是
3. 什么时候重构
相关信息
这更依赖当事人个人的判断,但仍有可参考的指引:
- 代码变更非常困难且容易引入 bug
- 充满面条代码,没有合理的分层
- 代码无法进行单元测试
- 以我这边账单中的 case 为例,项目迭代了近 5 年,代码中充斥着各种坏味道:
二、事前准备
以接手存量业务的背景来介绍,在开始重构前应该要做的哪些事情
1. 通读代码 & 产品功能梳理
- 这是要迈出的第一步,通过这个步骤,可以对项目有全面的了解,功能全貌、现状、项目依赖关系、组织结构等,这在后续做重构计划时至关重要
- 以梳理账单业务为例:
- 标记不同功能模块的重要程度
- 标记不同功能模块的状态、待确认项等
2. 抓住主要矛盾
- 通过通读和产品功能的梳理,基本上能够了解到需要治理的「重灾区」,为了保障重构工作的顺利推进及完成,还需要将「主要矛盾」拎出来作为重构的目标之一,以我对账单重构的梳理为例:
- React 技术栈
- 账单状态流转的面条代码
- 账单首页、月账单页缺少领域模型
3. 做减法
- 在梳理产品功能的同时,还需要关注
- 将它们标注出来,并在重构开始前把它们删除
- 这样一方面能降低代码整体的复杂度,也能使功能逻辑更加清晰,关注在需要关注的代码片段上
三、制定目标 & 方案
- 通过第一个步骤,大致确定了重构的范围,但要将重构工作顺利推下去,还需要制定好目标,并通过目标驱动的方式来落实重构方案
1. 目标
- 大型重构不可能「一步到位」,往往需要多人协同同时,将不同功能模块、领域分阶段推进
- 在目标制定上可以遵循 SMART 原则
- 这里主要分享下可实现原则、可衡量原则:
相关信息
:指制定的目标是可以被实现的
- 可实现有两层含义
- 一个是目标拆分与分工的合理性,即:目标的拆分会不会造成分工的混乱,可以实现多人共同协作
- 另一个则是指可衡量目标的合理性,是否可以被达成
相关信息
:这里指的是技术侧的可衡量原则,不与任意的业务目标挂钩,一般可以分为定性和定量可衡量
- 定性可衡量难以数字化度量,但是可以显性化性质度量的目标
- 比如:「高复用度、高扩展度」,定性的目标「抽象领域模型,实现状态流转、文案的复用,同时结合工厂模式等提升其扩展度」是可以被明确判断是否达成的
- 定量可衡量,则很好理解,可以在上面的基础上追加「账单首页、月账单页的卡概览相关逻辑用同一领域模型承接」
2. 方案
注意
前端项目一般没有后端那么高的系统复杂度,因此可能不需要做各种架构图,但是通常会包含以下内容:
- :大型技改一般需要多人协作,合理的分工能提升整体效率
- 举例:账单技改中将账单首页和月账单页分配给同一个人处理,因为这两个页面在状态流转、状态-文案处理上高度趋同,这样做更有利于模型抽象
- :合适的范围能够有效的控制项目风险以及能够激励我们持续的推进下去
- 举例:账单的一期技改聚焦在账单首页和月账单页,其他页面仅技术栈的迁移,保持现有逻辑不动
- :重构涉及大规模的改动,很难不出 BUG,详尽的监控可以帮助我们更早发现问题,风险更加可控
- :一方面管理项目风险,一方面帮助我们做更合理的范围拆分(即在一定的时间范围内,强迫我们决策什么应该做,什么不应该做),避免「无底洞」式投入
- :「工欲善其事,必先利其器」,不必多说,好的工具集能够帮助提高效率的同时降低风险
- :
- 统一注释规范:统一的注释规范有助于你(或协作同学)更快 get 到上下文、待办事项等,这块比较开放,项目组同学统一认知即可,如:使用 TODO、FIXME 进行不同类型注释
四、争取关联方的支持
- 有了以上几步的准备,便可以安心地去争取关联方的支持了,他们包括:
- PD(们):事情的「合法」性
- 测试:质量的最后一堵墙
- PM:排期
- 这一个步骤需要我们换位思考,讲清楚重构之后对每一个「我」的价值,形成「代码治理-价值传递」的正反馈,以下代入不同角色会关心的问题类型:
- PD:迭代效率,产品体验
- 测试:对代码质量的提升,后续测试工作量的投入
- PM:重构的背景、价值以及 PD、测试的支持
- 以上角色中,重点说服 PD、测试同学,当这两方都搞定了,PM 这一侧更多只是顺水推舟
相关信息
根据账单侧的实践经验总结了以下几点:
- 大型重构很多时候是无法争取到单独排期的,一般大型迭代是做重构的绝佳时期,但这种窗口可遇不可求,当没有这种契机时,可以按照产品规划,尽量挖掘出重构对后续事项的价值点
- 测试、PM 在排期上往往只是配合,最重要的还是 PD 的支持(优先级等都是 PD 去 PK 的),对于 PD,理性的价值探讨和 ,都是非常有必要的
关联方的预期管理
- 大型重构期间,避免不了会对需求迭代产生或大或小的影响,这里需要提前跟 PD 沟通好,避免在双方认知不一致的情况下发生某些分歧,导致彼此信任度下降
五、发布前
1. 切流方案
- 需要包含 三个部分,以账单重构的切流方案为例:
六、回顾
整个重构从 2 月份 到 9 月份历时 7 个月,中间遇到了很多问题无法一一报告,挑其中比较困难的点
:原计划第一版重构是在 5 月份发布,但是由于当时各种高优先级项目不断插队,导致重构的代码始终没办法推进至测试环节,期间找 PD、PM、测试 各种对焦、刷脸都争取不到测试资源,这导致整个上半年几乎没有结果,好在下半年抓住了一些关键的节点(业务规划、测试节奏安排等),在与 PD、PM、测试的极力争取下,把第一个重构版本推了上去
:单纯的架构持续演进并不算是困难,但在当时账单重构第一个版本刚上线,整个人心力交瘁的情况下,又马上决定在架构设计层大动干戈(第二版重构几乎完全重新设计了核心模块 domain)则不是一件易事(心智负担大、工作量大)
除此之外,在开始前、过程中也有一些担忧:
- :从现实角度讲,决定做重构不是一个理性的决定,它是个苦差,讲大白话就是「对绩效没啥帮助」
- :除了 ROI 太低外,更重要的它是一件高风险的事情,一旦出问题,还会对绩效「反向帮助」,属于「吃力不讨好」
对于这两点,也是开始前、过程中最纠结的点,但最终说服了自己,我给自己的答案是: