
1. 为什么 GitHub 选择让 AI 亲手改自己的代码1.1 这次重构到底改了什么先把这个项目的范围说清楚。三周、128个PR、83万行代码这几个数字放在一起确实唬人但你需要知道的是这不是一个“按下按钮AI自动重写”的黑色魔法而是一场有纪律、分阶段推进的代码大规模迁移。GitHub 内部实际在做的有两类事情一部分是把早期遗留下来的 Ruby 代码迁移到 Go另一部分是围绕 C 等语言的安全扫描与分析引擎做内部改造其中涉及 CodeQL 自身技术栈的升级调整。83 万行对应的是迁移所涉及的总代码量128 个 PR 是迁移过程中生成、经过人工评审后合入主分支的改动单元三周则是从第一批 PR 创建到最后一个 PR 合入的时间跨度。我看完这个案例的第一个感受不是“AI 好强”而是“工程管理好强”。因为任何一个经历过大型代码迁移的人都清楚83 万行代码本身不是瓶颈真正的瓶颈在别处怎么保证改完语义不变、怎么让众多工程师不互相踩脚、怎么在迁移期间让产品功能持续上线。GitHub 做的事情是把这三件事拆成了可执行的机制而不是靠一腔孤勇去硬冲。这个思路本身才是整个项目最有参考价值的部分。1.2 为什么偏偏是这个时间点动手“为什么是现在改”这个问题比“怎么改”更有意思。GitHub 的代码库不是一天变老的Ruby on Rails 那套技术栈在 GitHub 跑了十几年积累了海量业务逻辑。为什么偏偏是最近开始动刀我的判断有三个原因。第一技术债的累积到了临界点。老代码里充满为了兼容旧 API 而存在的适配层新功能每加一个特性都要在这些适配层上打补丁团队的实际开发效率已经被拖累了。第二AI 编程工具的成熟度发生了质变尤其是 CodeQL 这种把代码当作数据库来分析的工具加上 Copilot 的代码生成能力让单人完成大规模改造的产出大幅提升。过去一个工程师改 83 万行可能要按年计算现在按周计。第三GitHub 本身就是卖开发者工具的公司如果自己的产品链连自家代码库都驾驭不了对外讲故事就没有说服力。这种“吃自家狗粮”的姿态天然带有产品验证的味道。这几点放在一起构成了一个决策上的“完美窗口”问题足够痛、工具足够熟、团队足够有动力。如果你所在的团队正在犹豫要不要用 AI 做代码重构这个案例给出的参考是先确认你的代码库是不是真的痛到需要动刀如果只是“看起来有点乱”AI 重构反而会引入新的风险。2. CodeQL 上场用查询语言描述“哪里要改”而不是靠正则硬搜2.1 CodeQL 的工作逻辑代码即数据库CodeQL 最早是语义分析公司 Semmle 的产品GitHub 收购后把它变成了自己的安全扫描与分析引擎。它的核心思路可以这样理解普通工具把代码当作文本靠正则表达式去匹配“代码长什么样”CodeQL 把代码当作数据库先把代码解析成语义关系图——类、函数、调用关系、数据流、控制流——然后你用类似 SQL 的查询语言去问“哪些地方满足某个条件”。举个例子如果我们要找“所有调用了已废弃 API 且在循环体内执行的代码”用正则几乎写不出来因为你需要在文本之外理解调用关系和执行上下文。CodeQL 可以把这个问题转化成一个很自然的查询先定义“废弃 API 的调用点”再限定“该调用点位于循环体内”然后针对每一个命中点返回精确的文件路径和行号。这种查询能力正是大规模重构最需要的东西。GitHub 选择 CodeQL 作为重构的“眼睛”是很有道理的。因为大规模迁移最难的从来不是写新代码而是在 83 万行旧代码里精准定位所有需要修改的位置并且保证没有漏网之鱼。正则做不到人工 Review 83 万行更不现实。而 CodeQL 的查询结果可审计、可复现每一次命中的位置都可以回溯到某条查询规则这套机制为后续的自动化流程打下了基础。2.2 与正则、Codemod 等传统改造方式的差异很多团队做代码迁移用的还是“全局搜索 人工判断”的路子好一点的会用 Codemod 类工具在抽象语法树AST层面做模式匹配和替换。这两种方式跟 CodeQL 这类语义分析工具相比差别到底在哪里我整理了一个对比方式匹配粒度能否理解调用关系误报率适合场景正则全局替换文本否很高简单 API 改名无重载CodemodAST 模式语法树部分中等同语言内批量重构规则明确CodeQL 语义查询语义关系图是低规则写得好时跨语言迁移、依赖调用链的改造纯人工 Review主观判断是取决于人最终兜底不可规模化正则的问题很明显它不知道代码里有个类有多个同名方法一个属于公开 API 包另一个属于内部实现包全局替换会把不该改的地方也一起改掉。Codemod 比正则有进步能识别语法结构但同样不太理解“谁调用了谁”。CodeQL 的优势在于查询天然能结合调用图和数据流用语义而非语法去找改点。需要提醒的是CodeQL 也不是银弹。它的学习曲线比正则和 Codemod 陡峭不少查询语言的调试不像普通代码那样直观一个 ql 查询写错了命中结果可能差之千里。GitHub 内部有大量擅长 CodeQL 查询的专家这是普通团队不具备的。如果你想把这套流程复制到自己的项目里最现实的做法是先用 CodeQL 做“找改点”再用脚本或者 AI 辅助生成具体改动而不是一上来就把整个迁移都押注在 CodeQL 上。3. 128 个 PR 如何拆解大规模迁移的小步快跑策略3.1 一个 PR 对应一个独立语义变更128 个 PR 和 83 万行代码放到一起很多人第一反应是“每个 PR 平均改动六千多行”。但在真实工程里这个推测大概率不成立——PR 总数是按语义变更的独立性来划分的很多 PR 的改动量远小于平均值关键在于每一个 PR 都对应一个可以独立验证的语义单元。我经历过一次不太成功的代码迁移当时把所有改动塞进了一个超大 PR代码 Review 花了三周合并之后集成环境全是冲突。那次教训让我明白了一个道理代码评审的效率取决于评审者每次需要理解的语义边界有多大而不是单纯看改动行数的多少。一个 PR 只做一件事评审者不需要反复切换上下文测试验证也更容易收敛。GitHub 的 PR 拆解逻辑应该是先按照调用链把迁移任务切分成若干“代码切片”每个切片在入口和出口上保持相对独立一个切片对应一个 PR。切分完成后每个 PR 的评审者只需要回答一个问题在入口参数不变、出口行为不变的前提下这个切片的实现方式是否合理。边界清晰了评审自然快。这正是工程学里“分而治之”思想在代码迁移上的应用。3.2 依赖顺序与批量生成 PR 的流水线拆完切片之后还有个绕不开的问题这 128 个 PR 之间存在依赖关系。底层库没改完上层服务改了也合并不进去。GitHub 的解决方案是先把依赖关系画成一张有向图只有入度为 0——也就是没有被其他 PR 依赖——的改动才允许先行创建和合入然后逐层推进。这个依赖排序让人想起一个比喻换掉一副多米诺骨牌时不能从中间抽只能从第一张开始推。底层依赖就是那张最前面的骨牌它的改动影响所有上层模块所以必须先改、先测、先合入上层业务代码的改动是被动跟随的位置越靠后改动的不确定性越小。实际执行中批量生成 PR 还需要一套自动化流水线。我比较推荐这种流程用 CodeQL 跑全量扫描生成所有改点清单标注每个改点所属模块和依赖方向按依赖方向对改点分组输出 PR 创建计划用脚本按计划批量创建 PR每个 PR 自动关联测试计划并指派给对应模块的维护者PR 合入后自动触发下游模块的重新扫描与 PR 创建形成链式推进。这套流水线最关键的一步是第 2 步的依赖识别。这一步做得粗糙后面会产生大量合并冲突。GitHub 能在三个星期内把 128 个 PR 全部合入依赖规划这部分肯定是做了扎实工作的。我自己实践中也发现依赖识别阶段的投入产出比是最高的——把时间花在这里后期至少能省三倍的返工时间。4. Copilot 和 CodeQL 配合的实际写码工作流4.1 人工写查询、AI 生成改写、工程师校验现在聊到最让人兴奋的部分AI 到底在哪个环节写了代码答案可能不如你想象的科幻——不是 AI 自己从头写一遍而是 AI 在给定边界内生成改写方案边界由 CodeQL 查询给出最终的决定权在工程师手里。我把这个工作流还原成四步。第一步工程师写 CodeQL 查询把“需要改动的代码范围”精确圈定这一步产生的是改点清单。第二步把查询命中的代码片段交给 Copilot同时附上明确的要求说明旧接口的废弃原因、新接口的使用方式、以及转换规则。第三步Copilot 生成改写代码工程师逐条审查遇到模糊的 case 手动调整。第四步改动合入后再跑一遍 CodeQL 查询确认命中点数量归零用来证明“该改的确实都改了”。这个流程里最容易被低估的是第二步的需求描述质量。很多人以为给 AI 的需求描述越详细越好但在代码重构场景里真正重要的不是描述而是约束。比如你告诉 AI“把 A 接口替换为 B 接口B 接口的第三个参数是新增加的超时时间其余参数语义保持不变”这个描述比“请把旧接口改成新接口”有效得多因为它把不确定性压缩到了最小。4.2 复刻这套流程时的实操细节如果你想把这套流程在自己的项目里落地我的建议是先小范围试点。我自己做类似改造的时候会先在模块里挑一个改动量适中、语义边界清晰的子任务跑通整个循环再铺开到全量。这里有一个比较实用的操作清单需求描述里必须包含“旧代码触发问题的具体原因”否则 AI 大概率只会做机械替换不会理解为什么改给 AI 看的示例代码不要只给“改前”和“改后”两张图还要给“为什么这么改”的简要注释这个背景信息直接影响生成质量对 AI 生成的代码不要直接信任重点检查边界情况空值处理、类型转换、异常分支所有 AI 生成的改动都要走正常的代码评审流程不能因为是 AI 写的就降低标准每次评审后记录最常见的 AI 错误模式反向补充到需求描述模板里让后续生成的质量逐步提升。GitHub 在这个项目里还有一个容易被忽略的细节他们会用 CodeQL 的查询结果反过来校准 Copilot 的重写建议。当 AI 生成的代码与 CodeQL 命中的预期不符时工程师把修正版本反馈给系统让后续生成结果更贴近代码库的实际风格。规则引擎与生成模型相互校准这种机制我觉得是未来大规模 AI 重构的重要方向因为它本质上是在给 AI 建立代码库的“地心引力”——生成结果不是天马行空而是被项目本身的语义结构约束着。5. 改完 83 万行代码之后验证与兜底机制5.1 靠什么确认“改对了”83 万行代码不是改完就算数核心问题是你怎么知道改对了GitHub 的验证策略可以拆成四层每一层解决一个不同的问题。第一层是编译与单元测试。这层最基础抓的是语法错误、类型错误、接口签名不匹配这类低级问题。第二层是 CodeQL 查询复查。迁移完成后再跑一次当初用来定位改点的查询正常情况下命中数应该归零如果还有命中说明存在漏改的地方。第三层是集成测试与端到端测试。这层验证的是多模块协同工作时的行为是否一致许多跨模块的问题在这一层暴露。第四层是生产环境的灰度监控把代码逐步放到真实流量中观察错误率、延迟、资源消耗等指标有没有异常波动。这四层缺一不可。我见过一些团队前两层做得很好结果上线第一天被生产环境打爆原因就是第三层和第四层没跟上——单元测试通过只能说明改动的代码本身没坏不能说明整个系统在真实流量下依然健康。尤其是在 AI 改写的场景里测试覆盖不到的分支往往就是风险潜伏的角落所以灰度观察不是可选项而是必选项。5.2 回滚机制与灰度策略大规模重构最怕的不是改错而是改错了无法快速恢复。GitHub 这次迁移能推进这么快一个前提就是他们拥有成熟的分层回滚机制。设计回滚机制时核心原则是让每个 PR 都具备独立回滚的能力。也就是说任何一个 PR 出问题都可以单独回滚而不影响其他已经合入的改动。这个要求在 PR 拆解阶段就要想清楚——如果两个 PR 的改动密不可分回滚其中一个就必须连坐另一个那回滚的代价就会成倍放大。灰度策略方面GitHub 的做法是把不同模块的切换拆到不同时间点而不是一次性全部切换。每次切换都伴随一段观察窗口只有观察期指标稳定后才继续推进下一个模块。这个策略在工程上很朴素但在大规模重构里是关键保障——它把一次大爆炸拆成了多次小验证任何一次小验证失败损失都控制在局部不至于牵连整条链路。6. 大规模 AI 重构的边界那些绕不开的坑6.1 语义不变的灰区改错但测试通过的场景聊完成功路径我想聊聊这个项目里一定会存在、只是没有被放大呈现的灰区问题。代码重构有一个理论目标叫语义不变——也就是除了你打算改变的那部分行为其他行为一个字都不能变。但在真实代码里语义不变是很难严格证明的性质尤其是当代码涉及并发、缓存、超时、随机数这类非确定性行为时。举一个典型例子旧代码里 HTTP 客户端的默认超时时间是 30 秒新接口把超时时间暴露成了可选参数AI 生成的代码如果没有显式传入 30 秒单元测试根本跑不到超时场景生产环境却可能在某个突发流量下炸出问题。这种“改错了但测试通过”的场景恰恰是大规模 AI 重构最大的隐性风险。GitHub 能把这个风险控制在可接受范围内靠的不是 AI 更聪明而是代码评审、CodeQL 复查、灰度监控这套工程护栏。换句话说AI 提高了重构的吞吐量但工程护栏决定了下限。如果你只上 AI 不做护栏等于是给重构装了个大马力发动机却不装刹车。6.2 对普通开发者的启示最后说几点我自己的判断。这个项目给行业最大的启示不是“GitHub 很牛”而是 AI 重构的正确姿势是把任务拆到 AI 能处理的最小粒度。你让 AI 直接重构整个项目它大概率会给你一堆无法验证的改动但你让 AI 替换某个接口的所有调用点、保持外部行为不变它就能干得又快又好。对我们普通开发者我有几个比较实用的建议。第一先把项目里重复度最高的机械改动收集起来这些是 AI 重构的最佳试验田因为它们规则清晰、风险低、效果又肉眼可见。第二在项目里引入代码分析工具不一定是 CodeQL开源的 Semgrep、SonarQube 也行先把“自动找改点”这个能力建立起来再谈用 AI 写代码。没有可靠的分析工具做支撑AI 生成的改动就是无源之水。第三任何 AI 生成的改动都要走评审流程并且要保留“AI 改了什么、为什么这么改”的审计记录。这个习惯在项目出问题时会救你的命因为你能迅速定位到具体 PR然后利用前面设计的独立回滚机制快速恢复。我之所以把这个项目看作是近几年 AI 工程化落地里最有参考价值的样本不是因为 83 万这个数字本身有多震撼而是因为它展示了一条“工具链、流程、人”三者配合的可行路径CodeQL 负责定义边界Copilot 负责在边界内生成工程师负责评审兜底三者各司其职。能批量改 83 万行代码的 AI 确实很厉害但真正值得学习的是那个把 83 万行拆成 128 个可验证 PR、并在三周内安全交付的工程体系。这套体系才是比任何模型能力都更难复制的核心竞争力。