资讯动态

AI重构83万行代码:三周128个PR的工程治理实战

发布时间:2026/9/24 22:47:55 来源:尧图企业网站定制
1. 这件事到底在讲什么三周、128个PR、83万行代码的真实含义先把数字拆开看。三周大约是15个工作日128个PR平均每个工作日合并8.5个83万行代码平均每个PR约6500行。这三个数字放在一起任何一个有代码评审经验的人都会先愣一下——因为按传统工程节奏一个中等规模团队一天能认真评审并合并的PR数量通常是个位数而且单个PR超过800行就已经让评审人开始头疼了。所以这组数字真正指向的不是AI写代码很快这种已经被说烂的结论而是一个更具体的问题当一个大型开源项目决定让AI深度参与自身代码库的重构时工程流程要怎么改才能让83万行改动不把主干分支搞崩。这里的关键词是GitHub、Copilot、Rust、TypeScript、AI。把它们串起来故事线大致是这样的一个以TypeScript为主的大型前端/工具链代码库借助Copilot这类AI编程助手在短时间内完成了大规模的类型系统迁移、模块重构或者语言层面的替换而Rust很可能出现在性能敏感模块的重写或者工具链底层组件的替换上。128个PR不是128次零散提交而是一套被刻意设计过的、可并行、可回滚、可验证的改动单元。我先把结论摆前面这件事的难点从来不在AI能不能写出这83万行而在于人怎么设计一套流程让AI写出来的东西能被安全地信任。前者是模型能力问题后者是工程治理问题。绝大多数团队卡在后者而不是前者。这篇文章适合三类人看一是正在考虑把AI引入日常开发流程的工程师想知道真实落地时哪些环节会出问题二是技术负责人需要判断AI大规模改代码这件事的风险边界在哪三是对Rust、TypeScript迁移感兴趣的人想看看大规模重构在工程上是怎么被拆解的。不管你是刚入门还是已经带过团队下面这些内容应该都能对上你的某些实际困惑。2. 为什么是重写自己而不是新写一个功能2.1 自举式重构的特殊性给一个空目录让AI生成一个新项目和让AI去改一个已经运行了多年、有几十万行存量代码、有大量隐式约定的项目完全是两码事。前者AI只需要满足能跑后者AI必须满足改完之后原来能跑的东西还得能跑而且行为不能变。这就是重写自己的核心难点。存量代码里藏着大量没有被文档记录的知识某个函数为什么要在特定条件下返回null而不是抛异常、某个模块为什么绕了一圈才调用底层接口、某个类型断言为什么看起来多余但不能删。这些知识不在代码注释里而在提交历史和issue讨论里甚至在已经离职的工程师脑子里。AI在处理这类代码时最大的风险不是写错语法而是**看起来合理但语义漂移**。比如把if (x ! null)改成if (x)在TypeScript里对大多数情况等价但如果x是数字0或者空字符串行为就变了。这种改动在代码评审时极难被发现因为它太自然了。所以整个项目的设计思路第一步不是让AI动手而是先把什么算正确这件事定义清楚。这通常意味着测试覆盖率要足够高类型检查要足够严构建产物要有可比对的基准。没有这三样AI改出来的83万行就是83万个定时炸弹。2.2 为什么选Rust和TypeScript这两个方向从热搜词能看出Rust和TypeScript是这次改动的两个主要技术面。这两个选择背后有很现实的工程理由。TypeScript方向大概率涉及的是类型系统收紧或者模块系统迁移。热搜里出现了baseurl已弃用moduleresolutionnode10已弃用TypeScript 7.0这些词说明项目在做面向未来的配置清理。这类改动的特点是单点改动小但涉及面极广几乎每个文件都要动。这正是AI擅长的场景——规则明确、模式重复、可以批量处理。Rust方向热搜里有rust taurirust asyncrust 使用sqlx对mysql编程ch32使用rust开发这些词覆盖面很杂但共同点是Rust正在被用于替换原有的性能瓶颈或者跨平台组件。Tauri是典型的用Rust替换Electron的路径async和sqlx指向后端/数据层ch32指向嵌入式。一个项目同时出现这些词说明Rust的引入不是单点实验而是系统性的技术栈调整。为什么这两个方向适合AI介入因为它们都有强约束。TypeScript有编译器Rust有借用检查器。AI写出来的代码如果违反约束编译直接失败不会悄悄溜进主干。相比之下如果让AI去改一个纯JavaScript项目或者Python项目没有静态检查兜底风险会高一个数量级。这里有个很多人忽略的点AI编程的可靠性很大程度上取决于目标语言的静态检查能力。语言越严格AI越安全。这也是为什么Rust和TypeScript是当前AI辅助重构最活跃的两个领域。2.3 128个PR这个数字是怎么来的128这个数字不是拍脑袋定的。它反映的是改动被切分的粒度。如果按文件切83万行可能对应几千个文件PR数量会爆炸如果按模块切可能只有十几个PR但每个PR大到没人敢评审。合理的切分逻辑通常是这样的每个PR对应一个可独立验证的行为单元。比如把所有使用旧配置项的地方替换成新配置项是一个PR把某个模块的类型定义从any收紧到具体类型是另一个PR。每个PR都能单独跑测试、单独回滚互不依赖。128个PR意味着平均每个PR约6500行这个粒度其实偏大了。我推测实际情况是一部分PR是机械替换几千行但逻辑简单一部分PR是核心逻辑改动几百行但需要仔细评审。两者混在一起平均下来才是6500行。真正需要人仔细看的PR数量应该远少于128。3. 让AI改代码之前必须先搭好的四层防护3.1 第一层测试基线没有它一切免谈在让AI动第一行代码之前必须确认一件事当前主干的测试通过率是100%而且测试是可信的。什么叫可信就是测试真的在验证行为而不是在验证实现细节。我见过太多项目测试写了一大堆但全是expect(fn).toBeDefined()这种废话断言。这种测试对AI重构毫无价值因为AI把函数改成返回undefined它也能过。真正有用的测试基线需要满足几个条件。第一核心业务路径有端到端测试覆盖不是单元测试mock出来的假通过。第二有快照测试或者黄金文件对比能捕捉输出层面的细微变化。第三测试执行时间可控最好能在几分钟内跑完否则128个PR每个都跑半小时测试光等待就耗掉一周。实际操作中我建议先做一次测试体检随机挑20个核心模块手动改坏一处逻辑看测试能不能抓到。抓不到的说明这块测试是摆设得先补。3.2 第二层类型检查与Lint的严格模式TypeScript项目要把strict打开noImplicitAny、strictNullChecks、noUncheckedIndexedAccess这些能开的都开。Rust项目要把clippy的警告级别调高#![deny(warnings)]该加就加。这一步的意义在于把AI可能犯的错从运行时才暴露提前到编译时就拦住。AI写代码有个特点它会倾向于生成能通过当前检查的代码。你把检查调严它就会写出更严谨的代码你放松检查它就会偷懒。有个细节值得注意热搜里提到vue类型工具与现有TypeScript 7不兼容这类工具链兼容性问题在AI批量改动时会被放大。因为AI不知道某个类型工具的内部实现有坑它只会按类型签名去用。所以在大规模改动前先把工具链版本对齐、把已知的不兼容点列成清单是非常必要的。3.3 第三层构建产物的可比对性这一层经常被忽略但极其重要。AI改完代码后构建出来的产物应该和改之前行为等价。怎么验证最直接的办法是对比构建产物的哈希或者对比运行时输出。对于前端项目可以对比打包后的bundle在相同输入下的渲染结果。对于后端项目可以对比相同请求下的响应。对于CLI工具可以对比相同参数下的stdout。我自己的做法是准备一组回归用例覆盖典型输入、边界输入、异常输入三类。每次AI改完跑一遍回归用例输出和基线对比。不一致的地方人工判断是预期内的改动还是意外的行为漂移。3.4 第四层人工评审的抽样策略128个PR不可能每个都逐行看。但完全不看又不行。合理的策略是分层抽样PR类型占比估计评审策略机械替换类约60%抽查10%重点看是否有遗漏或误替换类型收紧类约25%抽查30%重点看类型断言是否合理核心逻辑类约15%100%逐行评审必要时结对评审这个策略的核心逻辑是把人的注意力集中在AI最容易出错、且出错代价最高的地方。机械替换出错概率低但一旦出错影响面大所以抽查要覆盖不同模块。核心逻辑出错概率高影响面相对可控所以要全看。4. 实操流程从零到128个PR的完整拆解4.1 阶段一建立改动清单约2-3天不要一上来就让AI改代码。第一步是让AI帮你分析代码库生成一份改动清单。具体做法把代码库的结构、关键配置文件、依赖清单喂给AI让它输出哪些地方需要改、改成什么、为什么。这一步的产出是一份Markdown文档列出所有待改动点按模块分组标注优先级和依赖关系。这一步的价值在于把改什么和怎么改分开。人负责判断改什么对不对AI负责怎么改快不快。如果混在一起AI改错了你都不知道错在哪。我实测下来这一步用Copilot的对话模式或者直接用大模型处理代码库摘要都行。关键是产出要结构化最好能直接转成issue列表。4.2 阶段二小范围试点约2天从改动清单里挑一个影响面小、测试覆盖好、逻辑相对独立的模块让AI完整改一遍走完整个流程改代码、跑测试、提PR、评审、合并。这一步的目的不是产出代码而是验证流程本身。你会发现很多问题AI改出来的代码风格和项目不一致、测试跑不过是因为环境问题而不是代码问题、PR描述写得没法看等等。这些问题在小范围暴露比在128个PR里暴露要好得多。试点阶段要重点观察几个指标单个PR从生成到合并的平均耗时、AI改动的一次通过率、评审时发现的问题类型分布。这些数据会直接决定后面大规模推进的节奏。4.3 阶段三批量生成与并行推进约10天试点跑通后进入批量阶段。这时候的核心工作是编排而不是写代码。具体操作上我会这样做把改动清单按模块拆成任务每个任务对应一个PR。然后用脚本或者工具批量触发AI生成改动生成后自动跑测试和类型检查通过的进入评审队列不通过的打回重做。这里有个关键技巧不要让AI一次改太多。一个PR改一个模块改完验证完再改下一个。并行推进的是不同模块而不是同一个模块的不同部分。这样即使某个PR出问题也不会污染其他PR。热搜里提到的copilot vscode怎么不能用vscode copilot对话丢失这类问题在这个阶段会特别烦人。因为批量操作时工具的任何不稳定都会被放大。我的建议是准备一套降级方案。Copilot用不了的时候能不能直接用API调用模型对话丢失的时候能不能从本地缓存恢复上下文这些预案要提前想好。4.4 阶段四收尾与固化约3天128个PR合并完之后工作还没结束。要做三件事。第一全量回归。把所有测试、类型检查、构建流程完整跑一遍确认没有遗漏。第二清理残留。AI改动过程中会产生一些临时文件、废弃的兼容代码、注释掉的旧逻辑。这些要清理干净否则会变成技术债。第三固化经验。把这次改动中总结出的规则、脚本、检查清单整理成文档下次再做类似事情时可以直接复用。这一步最容易被跳过但价值最高。5. 踩过的坑那些文档里不会写的问题5.1 AI的过度自信问题AI改代码时最危险的不是它不会而是它以为自己会。它会在没有足够上下文的情况下自信地做出改动而且改得看起来很合理。我遇到过一个典型案例AI把一个函数的错误处理从抛异常改成了返回默认值理由是这样调用方更简单。从代码风格上看没毛病但业务上这个异常是必须抛的因为调用方依赖这个异常来触发重试逻辑。这种改动测试如果没覆盖到异常路径根本发现不了。应对办法在给AI的指令里明确写清楚不要改变现有行为并且把关键的行为约束列成清单。比如所有抛异常的地方保持抛异常所有返回null的地方保持返回null。指令越具体AI越不容易自作主张。5.2 上下文窗口的边界问题大模型有上下文窗口限制。当一个文件很大或者一个改动需要跨多个文件理解时AI可能会看不到关键信息然后基于不完整的上下文做出错误判断。这个问题在Rust项目里尤其明显因为Rust的trait实现和泛型约束经常分散在多个文件里。AI看到调用处但看不到trait定义就可能用错方法。应对办法把大文件拆小把跨文件依赖显式化。在给AI的指令里主动附上相关的类型定义和接口签名。宁可多喂一点上下文也不要让AI猜。5.3 测试通过不等于行为正确这是最反直觉的一点。AI改完代码测试全绿类型检查通过构建成功——然后上线出问题。原因在于测试覆盖的是你想到的场景而AI可能改变了你没想到的场景。比如AI优化了一个循环把for改成了map在大多数情况下等价但如果循环体里有副作用比如修改外部变量行为就变了。而测试可能只验证了返回值没验证副作用。应对办法除了测试还要做行为对比。用相同的输入跑改前和改后的代码对比所有可观测的输出包括日志、副作用、性能指标。这一步很笨但很有效。5.4 工具链的隐性依赖热搜里electron打包vue-tsc版本不兼容moduleresolution已弃用这些词反映的是工具链层面的问题。AI改代码时通常只关注业务逻辑不会主动去检查工具链配置是否兼容。但工具链问题一旦爆发往往是全局性的。比如TypeScript版本升级导致某个类型工具失效所有依赖这个工具的地方都会报错。应对办法在改动开始前先把工具链版本锁定把已知的不兼容点列成清单。改动过程中如果必须升级工具链单独开一个PR处理不要和业务改动混在一起。6. 常见问题速查与排查思路6.1 问题速查表现象可能原因排查方向AI改完测试失败行为漂移或上下文缺失对比改前后输出检查是否遗漏关键约束类型检查报错但代码看起来对工具链版本不兼容检查tsconfig、依赖版本、类型定义来源PR评审时发现风格不一致缺少代码规范约束在指令里附上eslint配置和代码风格示例批量生成时工具频繁失败工具稳定性或配额限制准备降级方案分批处理加错误重试合并后出现性能退化AI优化引入额外开销对比改前后性能基准定位热点部分文件被遗漏改动清单不完整用静态分析工具扫描全库交叉验证6.2 独家避坑技巧技巧一给AI的指令要负面清单和正面清单一起给。只说要做什么不够还要说不要做什么。比如不要改变函数签名不要引入新的依赖不要修改测试文件。负面清单能挡掉大部分AI的自作主张。技巧二每个PR都附上改动理由。让AI在PR描述里写清楚为什么这么改。这不是为了好看而是为了评审时能快速判断AI的理解是否正确。如果理由写得含糊说明AI自己也没想清楚这个PR就要重点看。技巧三保留改前的代码作为对照。不要直接覆盖而是新建文件或者用git分支隔离。评审时能直接diff比看改后的代码猜原来是什么要高效得多。技巧四把重复出现的问题固化成检查脚本。比如检查是否有any类型残留检查是否有console.log未删除检查是否有TODO未处理。这些脚本跑在CI里比人工检查可靠。技巧五控制单次改动的规模。我实测下来单个PR超过3000行评审质量就会明显下降。宁可多切几个PR也不要贪大。128个PR听起来多但如果每个都清晰可控总耗时反而比20个大PR少。7. 这套方法能复用到哪些场景这套流程不是只能用于AI重写自己这种极端场景。任何大规模、模式化的代码改动都可以套用。比如框架升级从Vue 2升到Vue 3从React class组件升到hooks从Webpack升到Vite。这些改动的共同点是规则明确、涉及面广、单点风险低但总量风险高正好适合AI批量处理加人工分层评审。再比如代码规范治理统一错误处理方式、统一日志格式、统一API调用封装。这类改动往往被无限期搁置因为没时间做。用AI批量处理能把这类技术债的清理成本降一个数量级。还有跨语言迁移把Python脚本迁到Rust把JavaScript工具迁到TypeScript。热搜里rust taurich32使用rust开发这些词反映的就是这类需求。迁移的核心难点是语义等价而AI在模式识别和批量转换上确实有优势前提是你有足够的测试兜底。我个人在实际操作中的体会是AI不是替代工程师而是把工程师从重复劳动里解放出来去做真正需要判断力的事——定义什么是正确、设计验证方案、评审关键改动。83万行代码听起来吓人但真正需要人动脑子的部分可能只有几万行。剩下的交给流程和工具。最后分享一个小心得每次大规模改动结束后花半天时间复盘把这次遇到的问题、用到的脚本、总结的规则整理成一份作战手册。下次再遇到类似任务直接翻手册能省掉大量重新摸索的时间。这份手册的价值会随着你做的项目越多而越高。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑