一次服务重构的完整复盘:从2天人工梳理到40分钟AI链路分析,我做了什么
去年Q4,我们启动了一个交易类核心服务的重构。这个服务跑了6年,经历了3次架构演进,代码里藏着无数“历史的馈赠”。最近终于告一段落,趁记忆还热,把整个过程完整记录一下。
01 先说结论
重构完成后,几个关键数据:
-
链路分析时间:人工2-3天 → AI辅助约40分钟(含人工审查) -
重构后接口P99延迟:从180ms降到45ms -
上线后回滚次数:0 -
最耗时的环节:不是写代码,是搞清楚老代码在干什么
如果重来一次,我会把80%的时间花在前期的链路梳理上,而不是急着写新代码。
02 为什么老服务的链路梳理这么难
我们重构的是C++多进程架构的交易服务,一次请求会跨4-5个独立项目,涉及C++、Go、Proto三种语言的来回对照。
拿其中一个支付结果回调接口举例,老链路是这样的:
外部回调通知 → HTTP入口层(解密/校验/反查/确定状态)→ 业务编排层(流程编排、下游调用聚合)→ 核心业务层(DB事务、状态流转、写入)→ 查询层(只读查询)
看起来层次很清楚对吧?但每一层内部都有坑:
入口层不只是收包转发,还要做解密、验签、多层报文解析、限流、单号反查。这些前置逻辑里任何一个字段的来源,都可能影响后面的分支判断。
核心业务层是最复杂的。写入前要先加行锁读当前状态,然后根据状态、金额、笔数等条件进入十几个 switch 分支之一,每个分支的字段更新规则都不一样。而且这些分支之间的前置条件还有交叉——A状态在金额大于X时走分支3,小于X时走分支7,但分支3和分支7对某个字段的更新逻辑又完全不同。
最要命的是历史包袱。有些逻辑是为了兼容某个已经下线的老版本App打的补丁,有些是绕过某个早已修复的bug的临时处理,代码里没有任何注释。
人工梳理这样一条链路,要跨4-5个项目逐个方法读,光是把链路理清、产出一份能评审的方案,就要2-3天。
踩坑记录:第一次梳理时,我们漏掉了
channel_type字段的一个分支条件。这个字段在老Proto里没有,但在代码里用于判断走哪条状态流转路径。新服务上线后,渠道C的回调一直报状态错乱,排查了两天才发现是这个字段没传。后来我们把它列入了Proto补充清单,并加了一条规则:所有影响分支判断的字段,必须在方案文档里显式列出,不管它在老代码里看起来多不起眼。
03 我们的方案:把重构流程拆成可复用的Skill
吃过亏之后,我们决定不再“人肉梳理”,而是把整个重构流程沉淀成一个Skill——用流程拆分、分层知识库和Harness Engineering三部分把重构标准化。
3.1 流程拆分:把重构拆成4个可标准化步骤
我们把链路分析拆成了4个Step:
Step 1 · 调用链追踪:从入口接口出发,自动追踪跨项目的调用路径,生成调用链拓扑图。这一步帮我们省了大量“翻代码找调用关系”的时间。
Step 2 · 逐层代码理解:按入口层→编排层→核心业务层→查询层的顺序,逐层提取业务逻辑。关键约束是设了成本上限——同一个文件最多读2次、参考项目最多查3个,避免在某段代码上反复纠缠。
Step 3 · Proto GAP分析:把老链路需要的每个下游能力和现有服务的Proto接口逐一比对,标记状态并列出要补的字段。这张表直接决定了Proto要改哪些、BO要补哪些实现。
Step 4 · 生成方案+拆分任务:按模板生成完整方案,并拆成跨项目的总任务列表。方案覆盖链路概述、原始调用链、数据依赖分析、DDD归属映射、Proto协议定义等十几个章节。
其中DDD归属映射是最关键的一张表——它把老链路的每个步骤明确归到新架构的某一层、某个服务。有了它,编码阶段就不用回头猜某段逻辑该放哪。
3.2 审查阶段:AI负责整理选择题,人负责做决策
方案生成后,AI会跑一遍完整性检查——方案文档的所有章节是否齐全、每个旧步骤是否都有归属、状态机分支是否都列出。
然后进入人工审查。这个阶段离不开人,因为有些判断只能靠业务经验:
-
一个 switch分支是废弃逻辑还是仍在生效? -
某个字段能不能安全去掉? -
灰度按什么维度切?
这些代码里读不出来。AI负责把这些取舍整理成清楚的选择题,做最后决策的还是人。
真实场景:审查时AI问了一个问题——“
channel_type会影响状态机的多个分支判断,老Proto里没有这个字段,是否需要列入补充清单?”这个问题直接点出了我们之前踩坑的地方。后来我们把这个字段加进了Proto,并且在clarifications.md里记录了完整的决策过程,后面换人接手时一看就明白。
3.3 编码阶段:分层设计让逻辑不被污染
方案定下来之后,编码基本是“照着图纸施工”。
DDD分层架构的调用方向是 Gateway → Service → Logic → Repo,其中领域层 Entity 不依赖 tRPC 等框架,Logic 通过接口使用 Repo 能力。这条规则让业务逻辑不被协议和基础设施细节污染。
我们在核心业务层用了一个Builder链式编排模式,让流程一眼能看清:
flowData, err := builder.
WithParamValidation(). // 参数校验
WithResolveOrderNo(). // 确定业务单号(特定场景反查)
WithQueryOrderDetail(). // 查详情
WithCheckOrderState(). // 状态校验(幂等)
WithVerifyTicket(). // 票据验证(非关键,失败不终止)
WithDetermineTargetStates(). // 确定目标状态
Build()
每个 WithXxx 是一个独立、可插拔的步骤,CR阶段会专门检查分层边界——Service里是否混入业务逻辑、Logic里是否直接使用PB类型。
实际效果:这个模式让我们在后续两个类似服务的重构中直接复用了整套流程,链路分析时间从最初的2天缩短到了40分钟左右(含人工审查)。当然,第一次做的时候花了将近一周来打磨Skill本身。
04 几个真正有用的经验
第一,方案文档比代码重要。 重构中最有价值的产出不是新代码,而是那份能评审的方案文档。它把隐性的业务规则显性化了——即使不重构,这份文档本身也是团队的重要资产。
第二,AI的价值在于“不漏”,不在于“快”。 AI不会比人更聪明,但它不会因为疲劳而漏掉第13个 switch 分支。我们用AI做初筛,人工做终审,效率提升主要来自“不用从头读一遍所有代码”。
第三,灰度策略比技术方案更需要经验。 代码里读不出来的东西——比如某个字段在特定渠道下才能安全去掉——只能靠业务经验判断。这部分AI帮不上忙,但AI可以把这些决策点清晰地列出来,让人集中精力做判断。
第四,沉淀Skill的回报是复利。 第一次做的时候,打磨Skill本身花了一周多。但到第二个、第三个服务重构时,基本可以做到“开箱即用”。目前我们已经用这套流程完成了5个服务的重构,平均链路分析时间从2天降到了40分钟左右。
本文部分内容参考了霍格沃兹测试开发学社整理的相关技术资料,主要涉及软件测试、自动化测试、测试开发及 AI 测试等内容,侧重测试实践、工具应用与工程经验整理。
- 点赞
- 收藏
- 关注作者
评论(0)