
128个PR、83万行代码、三周时间GitHub把“让AI重写自己”这件事从一个口号变成了公开数据。很多人的注意力都停在“AI写代码”这三个字上但我更关心另一件事83万行代码是怎么被拆成128个PR又在三周内安全合入主干而没有把整个仓库搞崩的。这篇文章从一个长期做存量系统重构的工程师视角把这套流程从拆解思路、AI介入方式、PR合流、测试保障到翻车现场完整复盘一遍。无论你是技术负责人还是正在研究AI辅助编码的开发者都能在里边找到自己项目里可以直接复用的部分。1. 项目全貌三周、128个PR、83万行代码是怎么拆出来的1.1 为什么要让AI重写存量代码先别急着聊AI回到最开始的问题一个正常运转的代码库为什么要冒风险重写我见过太多“重构一时爽事后火葬场”的项目所以拿到“AI重写自己”这个题目时第一反应是去看动机是否成立。存量代码最大的问题是技术债的复利效应。同一个分页逻辑在十几个模块里各写一份有人用LIMIT 10 OFFSET 20有人用limit(10).skip(20)还有人在SQL里硬拼字符串。这种代码不是不能跑而是每次改需求都要把所有副本找出来逐一同步。维护成本会随着模块数量指数上升最终超过新增功能的收益。这时候重写的价值就出来了不是“看代码不顺眼”而是把重复逻辑收敛成公共组件把隐式依赖改成显式接口。那为什么偏偏是AI来做因为这类重写有一个共同特征机械性极强、模式高度重复。AI非常擅长“照着旧行为写新代码”尤其是在目标模式明确的情况下。比如“把这段基于字符串拼接的查询改成参数化查询”“把这三个工具函数合并成一个类”这种任务让工程师手工做也能完成但会消耗大量低价值时间。AI参与之后初级工程师可以专注审查和补齐边界而不是泡在复制粘贴里。还要澄清一个误解标题里的“重写自己”不是推倒重来。真正发生的是代码现代化更接近一种受控的迁徙。旧模块被切成小块逐一转换成新结构每一步都能单独验证、单独回滚。83万行规模听起来吓人拆成128个PR之后单个变更的体量反而比很多日常需求还小。这也是整个项目能三周走完的前提——大工程不是一口气做完的而是被拆成很多小步快跑。1.2 128个PR的拆解逻辑与工作量判断为什么是128个PR而不是一个大PR或者500个碎PR这是项目启动时就要回答的问题。一开始就扔一个83万行的巨型PR代码评审根本没法展开。几千个文件的diff在屏幕上加载完都会卡顿更不用说CI要跑几个小时测试失败时连定位都困难出了问题回滚更是灾难。反过来说PR也不是越碎越好。每个PR都有固定成本触发一次CI、至少一次人工审查、一次合并操作。如果一个PR只改几十行那128个PR远远不够可能得拆成上千个管理成本和排队等待时间就会压过策略收益。128这个数字其实是“可评审性”和“管理开销”之间的平衡点。按83万行除以128粗略估算平均每个PR大约涉及6500行代码变更去掉注释和空行后真正有业务逻辑的改动大概是两千到三千行这个体量刚好在一两个工程师可以认真审完的范围里。拆解顺序上首先要画依赖图。哪些文件是最底层工具库哪些是业务模块谁依赖谁必须列清楚。重写不能从业务层开始因为底层结构没定上层代码很快会因为API变动反复返工。通常的做法是先重写无业务逻辑的基础层日期处理、字符串工具、数据结构封装这一类确认这块稳定后再推进数据访问层和对外接口最后才是业务流程编排。依赖图一旦确定每个PR的边界就顺理成章了。工作量判断也有一个简单公式可以套总行数 ÷ 单PR有效行数 × 单PR周期。假设一个PR从AI生成到人工审查、修反馈、测试通过需要半天128个PR单线程就要64个工作日显然三周内做不完。所以必须靠并行。把团队按模块分成几个平行小组每个小组维护自己那批PR共享底层的合并序列。我见过不少重构项目前期拆解做得很好最后却卡在“串行依赖”上所以这里提前说清楚三周83万行靠的不是个人英雄主义而是正确的并行结构。2. 核心引擎AI如何参与代码重写2.1 让AI重写代码的三种介入方式AI在这个项目里不是一个“自动写代码的按钮”更像一个可以持续对话的结对程序员。实际操作中有三种介入方式按自动化程度从低到高排列。第一种是“逐函数重写”。把旧代码块、目标接口定义、预期行为描述一起丢给AI让它生成新版实现。这种方式最可控适合处理逻辑复杂、边界条件多的核心函数。人需要做的事情是写清楚prompt然后逐行审查生成结果。用起来非常像把需求文档念给一个熟悉项目语法的工程师听。第二种是“文件级批量重构”。直接把整个旧文件作为上下文给出新风格规范让AI一次性重写全部内容。这种方式效率很高但风险也明显文件里的隐藏依赖、历史遗留的特殊处理逻辑AI可能照搬也可能自作主张。我在实际操作中会强制要求AI在重写时保留所有注释因为注释经常是理解边界条件的唯一线索。第三种是“AI Agent自主拆任务”。给它一个明确的最终目标、代码库的读取权限、以及一份验收标准让它自行探索并拆出子任务。这种方式适合那些模式重复、边界清晰的模块比如自动生成数据模型层、批量转换接口参数类型。但它不能失控我在项目里会设置一个硬性限制Agent做完一个文件、或做满一定数量的改动后必须停下来等人工确认不能一口气改完一组再提交。我自己的体会是三种方式经常混合使用。一个模块里如果8成代码是重复模式就用第二种或第三种批量处理剩下那2成有特殊业务逻辑的单独拿出来用第一种逐函数处理。这比“全交给AI”或者“全人工写”都快也稳。2.2 人工和AI的分工边界什么代码适合AI改不是所有代码都适合丢给AI重写。这里有一条我反复验证的判断标准如果这块代码的意图能够从注释、函数名、接口定义中直白读出来那它大概率适合AI反过来如果理解这段代码需要翻五个文件、查三次历史提交记录那就说明AI没有足够上下文人工必须先进场。适合AI的部分包括纯工具函数、参数校验逻辑、DTO和模型定义、重复的分页查询、标准化日志输出、按照接口文档生成客户端代码。这些代码的特征是输入输出明确几乎没有不可预期的副作用。AI只要看到接口签名和目标规范就能生成出质量稳定的结果。不适合AI的部分包括涉及状态流转的支付流程、并发资源访问、强一致性的数据迁移脚本、带复杂权限判断的业务规则。这些代码的“正确性”依赖于大量隐含约束比如某个操作必须在事务里执行、某个状态只能从特定入口进入。AI不会知道这些约束硬让它重写生成出来的代码看起来天衣无缝一上生产就是事故。实际操作中我会给不合适的代码做标记。标记规则很简单如果这段代码在过去一年里有超过一次线上故障记录、或者有专门的try-catch处理幂等那就列入“人工保护名单”。名单上的代码不参与AI批处理最多让AI做辅助建议真正的改动由人完成。这个保护名单是整个项目能安全落地的重要底座。3. 实操过程从单个PR到128个PR的合流3.1 单个AI重写PR的标准化流程当128个PR不是随机产生而是遵循同一套流水线时审查和追踪成本才会降到最低。我总结的单个PR流程分为五步AI生成 → 基础编译 → 人工语义审查 → 自动化测试 → 合并。流程里最关键的是第一步生成端的约束。要求AI每次只输出与本PR相关的文件不要顺手调整格式化风格不要“帮”你重构相邻代码。AI有个坏毛病会在完成指定任务后自作主张改善邻近代码这在批量重组时非常危险它会让diff变得不可控。我在prompt里会专门加一句“只修改指定范围内的代码不改变任何无关内容。”然后是PR模板每个AI重写PR必须包含四块内容变更摘要、风险说明、测试证据、回滚指引。模板写清楚后审查者一眼就能判断要不要深入看。一个可参考的模板是这样的## 变更摘要 - 重写范围src/services/order/checkout.ts - 重写前逻辑基于字符串拼接生成订单SQL存在注入风险 - 重写后方案使用参数化查询 ## 风险说明 - 影响接口/api/v1/orders/checkout - 变更点数据库查询构造方式完全替换 - 已知风险无原有错误处理逻辑全部保留 ## 测试证据 - 相关单测tests/services/order/checkout.test.ts新增12条用例 - 本地冒烟已跑通下单、秒杀、超时取消三个场景 ## 回滚指引 - 回滚命令git revert commit-sha - 依赖项无本PR不涉及数据库表结构变更这套模板的好处是把“AI是不是写对了”这个很难全局回答的问题转化成几个可以逐项验证的问题。审查者不用从头读所有代码先看风险说明里圈定的接口范围再对测试证据做抽查效率会高很多。3.2 128个PR的合并顺序与冲突控制128个PR不是随便哪个先合哪个后合合并顺序错了后面一半的PR都会反复冲突光是解冲突就能耗掉三周里的大部分时间。我的做法是画一张“合并路线图”把PR分成几个批次批次之间严格串行批次内部可以并行。第一批是基础工具层的PR通常只有十来个但所有其他PR都依赖它们。这一批必须最优先合完并且每合一个就要立即跑一次全仓库的回归测试确保底层变更没有破坏现有功能。第二批是数据访问层和接口定义层这一批会和第一批产生直接依赖但只要第一批的API没有频繁变动冲突仍可控。第三批是业务流程层这一批数量最多可以拆给多个小组合并。最后一批是清理层负责删掉不再使用的旧函数、旧引用、过期注释。冲突控制方面有几个操作细节值得说。所有PR在合并前都要求基于最新的main分支rebase不允许使用merge提交。合并窗口也有限制每天只在固定时间合并两批PR比如上午一批、下午一批其余时间只更新分支不合并。这样可以保证冲突集中出现而不是随机爆发。一旦某个PR因为底层变更导致冲突第一时间不是硬解而是查冲突文件是不是也出现在其他等待中的PR里。如果是那要考虑调整合并顺序避免所有PR都撞在同一个文件上。三周83万行的高强度决定了对合并没有太多回旋余地。所以在项目启动前一天我会把所有参与者的分支命名做统一规范pr-{模块}-{序号}并且要求所有人只从统一的基座分支切出不在别人分支上开自己的分支。这些看起来不起眼的规则实际决定了后两周的合流是否顺利。4. 保障体系如何在83万行代码变更中不翻车4.1 测试金字塔与CI自动化防线大量代码变更时最可依赖的不是人工审查而是一套能快速反馈的自动化防线。这次AI重写项目里测试策略按金字塔分了三层。底层是单元测试重写后的每个函数至少覆盖正常输入、异常输入、边界值三种情况。对AI生成的代码我会额外要求测试数量和改动行数成正比。比如一个函数重写后变化超过200行那新增测试用例不能少于10个。这不是形式主义而是AI生成代码最常犯的错误就是丢掉边界守卫单测是拦截这类问题效率最高的手段。中间层是合约测试和快照测试。重构过程中最怕的是“逻辑没变接口变了”。合约测试专门盯对外接口的输入输出格式一旦AI生成的代码改变返回结构测试立刻红灯。快照测试则负责UI渲染和序列化结果的比对专门抓AI“偷偷调整了输出格式”这种隐性变化。最上层是集成测试。重写涉及跨模块调用时光靠单测是不够的必须真实跑通一条完整链路。比如支付模块重写后要实际走一遍从下单到扣款到回写订单状态的全流程。集成测试不需要太多但每个核心链路至少要有一条否则就是拿生产环境当测试环境。CI流水线的检查项也要针对AI代码做特殊配置。常规的类型检查、lint、覆盖率检查必须有但要额外加三条检查是否存在未使用的变量和函数因为AI经常生成“预留”代码检查是否引入重复工具函数防止AI把别人的代码新复制一份检查注释是否覆盖所有导出的public函数避免重写后文档断层。这三条规则全部能通过我才会允许PR进入人工审查阶段。4.2 代码审查的节奏人该审什么、怎么审很多团队搞AI重构最担心的是代码出问题没人发现。我的经验是不需要让每个审查者从头到尾读每一行代码但要保证每个PR至少有一个真正理解业务的人认真读一遍关键diff。人工审查应该盯四件事。第一AI是否改变了原有业务行为特别是错误处理分支、默认值、超时时间这类“平时注意不到但一旦出事就致命”的细节。第二是否存在隐藏依赖被破坏比如某个工具函数在其他地方被以特定顺序调用重写后顺序变了。第三并发安全性AI很容易把同步代码改成异步逻辑时不加锁或者反过来。第四是否出现了“假重构”只是换了种写法底层还是那段已经有问题、需要淘汰的逻辑。审查节奏上我采用“分层审查制”。第一层是AI自审让另一个AI对生成的代码做静态差异检查专门标出可疑点。第二层是自动化工具前面说的CI检查。第三层才是人工。人工审查不需要覆盖全部文件但重点文件必须逐行看。我给自己定的规则是核心模块的大约百分之三十的文件我必须亲眼看完其余可以依赖工具加随机采样。为了不让审查成为瓶颈需要给审查者足够的上下文。每个PR关联的依赖图、必要的历史提交记录、以及旧代码的位置都要提前挂在PR描述里。别小看这一步很多审查卡壳是因为审查者还得自己翻代码找旧逻辑在哪一旦找不到了就只能草草approve。上下文越清晰审查速度越快质量也越高。5. 常见问题与排查经验实录5.1 AI重写代码的几个典型翻车现场项目做多了踩过的坑基本都能分类。AI重写代码的翻车现场我见得最多的是下面四类。第一类是调用了一个不存在的API。原因通常是AI从训练数据里记了一个相似库的函数名在项目里其实没有引入这个依赖。这个错误在编译阶段就能暴露风险不算高但容易让人放松警惕。难的是那些“编译能过但运行就挂”的版本比如参数顺序被悄悄调换类型又刚好兼容这类问题一定要靠测试覆盖。第二类是丢失空值保护。旧代码里常见的if (user ! null user.address ! null)在AI重写后可能被合并成user.address.city的链式访问。AI会默认数据永远合法但真实线上数据经常不合法。这个问题最隐蔽因为正常单测永远测不出来。我的对策是在prompt里明确要求保留所有空值检查并额外加一条规则AI生成代码里如果去掉了任何防御性判断必须显式说明原因。第三类是把旧代码的低效逻辑原封不动作了一遍“格式化搬运”。AI没有真正重写只是把代码拆得更碎行数翻倍性能特征原样保留。这类问题靠代码审查抓看到diff里大量新增但逻辑等价的内容就要怀疑是不是假重构。识别之后我会要求AI重新针对性能瓶颈做一次真正的方案设计而不是继续做文本级替换。第四类是把带副作用的函数改成了纯函数。AI看到函数内部有外部状态修改误以为这是坏味道自作主张改成返回新对象。表面上看更优雅实际上破坏了原本依赖这个副作用的调用方。这种问题单测如果只是分别测两个函数也能通过只有集成测试能把问题暴露出来。5.2 排查工具链与速查表面对83万行级别的变更问题排查不能靠肉眼扫描代码要有一套高效的定位方法。我把排查过程分成三个层面构建失败、测试失败、运行时异常。构建失败一般最容易定位。先看报错发生的文件确认是不是依赖的底层库还没合入这种情况直接查合并状态就好。排除依赖因素后看是不是AI生成的代码引用了不存在的符号。我常用的工具是编译器的错误输出配合全局搜索确认基本五分钟内能解决。测试失败要区分是单测失败还是集成测试失败。单测失败先看是不是测试数据本身过时AI重写代码后测试输入和代码行为不一致。集成测试失败优先级最高因为它往往意味着跨模块行为变化。这时我会把失败用例的调用栈贴回给AI要求它解释新旧逻辑差异同时人工去看最顶层调用方代码确认是不是边界条件被漏掉。运行时异常是排查成本最高的。线上偶发错误、数据不一致、性能劣化这些都很难在测试环境复现。我的经验是先把异常的错误信息聚合成关键字去diff里搜这些关键字的上下文看AI是否改动过相关分支。如果搜不到再从git历史里找出这个API最近几次的变更记录从时间线上缩小嫌疑范围。最后整理一个排查速查表方便实际项目中直接对照现象可能原因优先排查方法编译失败引用了不存在的API全局搜索符号定义检查依赖声明编译通过但运行抛异常参数顺序或返回类型被调换对照旧代码和生成代码的参数签名数据丢失防御性判断被移除搜索所有可能为空的链式访问接口返回值变化合约测试未覆盖对比新旧接口文档和实际返回结构大规模重复代码AI低效重写或“搬运”检查diff中是否只是格式化变化集成测试偶发失败并发逻辑被改变检查锁、事务边界、状态切换顺序这套速查表本质上是把常见问题从“看缘分”变成“按路径排查”。我自己的习惯是在项目中每天都留出固定时间把当天所有失败用例按表分类填一遍坚持下来后绝大多数问题都能在当轮解决不会滚到明天成为更大的债。最后再分享一个真实感受。AI重写存量代码这件事技术选型和工具配置只是表象真正决定成败的是你有没有把“不可控”变成“可控”。128个PR、83万行代码这个数字不是你给AI下一道命令就会发生的它是一套精密的拆解、合流、验证机制推着走完的。如果你也想在自己的项目里做类似的事我建议从一个小模块开始比如把那几个散落各处的分页逻辑收敛成公共组件跑通一个完整的AI重写PR流程。跑通之后你会发现AI并没有替代工程师做决定它只是把那些重复劳动消掉把人的精力逼到真正该花的地方去。