你有没有遇到过这种场面?

你让 AI 编程智能体给现有应用加一个新功能,比如商品搜索。你起身倒了杯水,回来一看,GitHub 上躺着一个 PR,改动 1721 行。里面塞了新的数据模型、seed 数据、API 路由、客户端接线、UI 组件,还有空状态和错误状态,全挤在一个 diff 里。

你盯着那个数字,第一反应不是「好快」,而是「我怎么审?」

我最近在帮 Channing 跑 AI 应用和 LangGraph 工作流,这种场面见得特别多。看多了之后,我越来越觉得,AI 写代码的速度早就不是瓶颈了。人类审代码的速度,才是新的瓶颈。

巨型 PR 不是代码问题,是工作流问题

GitHub 昨天发了一篇博客,标题就叫 Turn one giant AI-generated pull request to a reviewable stack。它把这个痛点摊得很开。

文章说,AI 编码智能体因为训练数据里都是人类多年写代码的习惯,天然倾向于把一件事「端到端」做完。你给它的提示词越像完整需求,它越容易交给你一份完整但巨大的 diff。数据、API、接线、UI 全包,一次到位。

听起来很爽,但审阅者就开始痛苦了。

作者列出的那条死循环,我觉得每个用 AI 写过代码的人都熟悉。

  • 改动超过一千行,reviewer 心里默默点下「稍后处理」
  • 人失去上下文,反馈质量下降,只能从表面挑问题
  • PR 越躺越久,冲突越来越多,最后只能欠审合并
  • 代码上线后留下一堆「当时没看清」的坑

这其实就是用速度换质量。AI 帮你把 coding 阶段的时间省下来了,但 review 阶段的时间一点没少,反而被压缩得更紧张。

GitHub 的解法不是让 AI 少写点,而是让写出来的东西更容易被人类消化。它把这个解法叫 stacked pull request,中文我管它叫堆叠式 PR。

核心思路,把一个大功能拆成依赖链

堆叠式 PR 的核心只有两个字,分解。

它把一个大功能拆成逻辑上前后依赖的几层,每一层只负责一个关注点。文章里的例子是给购物助手加商品搜索,拆成了四层。

L1 对应 feat/catalog-data,负责类型化的商品目录、seed 数据、验证规则和数据访问模块。这是地基,没它后面都立不住。

L2 对应 feat/search-api,负责带校验的 /api/products/search 端点。它依赖 L1,但只关心 API 契约。

L3 对应 feat/chat-grounding,负责让聊天组件调用真实 API,返回基于真实数据的答案。它依赖 L2。

L4 对应 feat/grounded-ui,负责产品引用卡片、loading 状态、空状态和错误状态。它依赖 L3。

你看,数据、API、接线、UX 被分开了。每一层都够小,小到审阅者能在脑子里装下。数据 owner 看 L1,后端 owner 看 L2,前端 owner 看 L3 和 L4,各看各的,互不干扰。

这跟我在 LangGraph 里设计工作流的感觉很像。一个复杂的 agent 工作流,如果所有节点都塞在一个巨大节点里,调试和评估都难。但只要你把它拆成状态节点、边、条件分支,每一步都可观测、可替换,整个系统突然就从黑盒变成白盒。代码审查也一样,stack 的每一层就是一个可观测的节点。

工具链,gh stack 和给 agent 的 skill

GitHub 现在把这套流程原生化了。你可以装一个 CLI 扩展。

1
gh extension install github/gh-stack

装完之后,几个关键命令就覆盖了整条链路。

gh init stack 用来初始化新栈,指定 base 分支。这个 base 很重要,因为后续每一层的 CI 和合并规则都会以它为基准。

gh stack add 在当前层之上加一层,依赖关系自动带过去。你不需要手动切来切去记哪个分支基于哪个分支。

gh stack push 把本地栈推到远程。

gh stack submit 为每一层创建 PR,并自动把依赖关系写进描述里。别人打开 PR 就能看到这个层在栈里的位置。

gh stack rebasegh stack sync 是维护栈用的。当 L1 被改后,上面三层都需要重新基于新的 L1。sync 可以一次性把变更向上传播,不用你手动一层层处理。

更妙的是,GitHub 还给 Claude Code 这类 agent 准备了一个 skill。你可以直接让 agent 安装 github/gh-stack。这样 agent 自己也能理解什么叫层、什么叫依赖、什么叫提交栈。这不是简单的 prompt 工程,而是把「分层交付」这个约定写进了 agent 能调用的工具里。

我一般在给 agent 派活之前,会先写清楚四层结构,每层都有一个退出条件。比如 L1 的退出条件是「数据模型有类型、有 seed、有验证、有单测」,L2 是「API 有输入校验、有稳定响应契约和错误响应」,L3 是「前端能调用 API 并展示结果」,L4 是「UI 有 loading、空状态、错误状态,且所有引用可点击」。agent 按顺序推进,每满足一层退出条件才进入下一层。这比一次性甩给它一个完整需求要稳得多,也更容易在它跑偏时及时发现。

当然这里有个坑得提前说。GitHub 网页版有一个「Rebase stack」按钮,看起来很方便,点一下就帮你 rebase。但它是在 GitHub 服务器上跑的,会把 committer 重置成点按钮的人,而且不会签名。如果你们分支保护要求签名提交,点一下可能就在沉默中把栈炸了。更安全的做法是本地跑 gh stack rebase,手动处理冲突,再 gh stack push

审阅顺序,从上往下读,从下往上审

文章给了一个很实用的口诀。

读的时候 top-down,先看最顶层。这样你能在第一时间知道最终目标是什么。比如最顶层是「在聊天界面里显示产品卡片」,你脑子里立刻有了全景图。

审的时候 bottom-up,从 L1 开始。地基不稳,上面盖什么都没意义。L1 的类型对不对、数据验证是否完整、查询 helper 是否安全,这些问题先确认。L2 的输入校验、响应契约、错误状态处理。L3 的每一次回答是否都能追溯到真实 API 响应。L4 的每个引用是否链接到真实产品、loading 和错误状态是否完整。

GitHub 博客里还提到一个细节,我觉得特别值得注意。L1 被 Copilot Code Review 扫出两个问题时,reviewer 只需要把反馈交给 L1 的作者。这个范围非常小,改完后用 gh stack sync 向上传播,上面三层的人不需要重新审他们已经看过的东西。这才是 stack 真正省时间的地方,不是省在写代码,而是省在反复重审。

这种顺序感,其实能缓解审阅者的认知负担。你不需要一次性吞下 1721 行,你只需要在每一层门口检查通行证。

这背后的深层变化,人类 reviewer 在变成什么

但我想把这件事再往前推一步。

堆叠式 PR 不只是「把 PR 变小」的技术技巧。它其实是在回答一个更尖锐的问题,当 AI 编码智能体成为主要作者,人类 reviewer 的角色会变成什么?

我的感觉是,人类会越来越多地从「逐行挑错」转向「定义验收标准和分层结构」。你先告诉 agent,这个功能要拆成四层,每层验收标准如下。agent 负责把每一层填完。你站在栈顶看一眼,然后一层一层往下按开关。

这种转变其实已经在发生。以前 review 是一份工作,以后 review 更像一种设计。你设计的不是每一行代码,而是代码的交付结构和验收方式。

对 Java、Spring 这种服务端背景来说,这也并不陌生。一个微服务系统,如果所有逻辑都写在一个巨大 service 里,谁也审不动。但只要你把领域层、应用层、接口层分开,每一层都有清晰的入参和出参,审查压力就会下降。堆叠式 PR 只是把这套分层思想,延伸到了 AI 代码的交付流程里。

我得承认,它不是银弹

写到这里,我必须诚实地说,这套方法也有明显局限。

第一,不是所有需求都能干净拆成四层。有些改动是横切关注点,比如要同时改 10 个模块的接口签名。硬拆反而会让依赖关系变得混乱,合并成本更高。

第二,它强依赖 CI。每一层都要能独立跑检查。如果 L1 的测试只能在 L4 的 UI 里跑通,那栈就白拆了。层与层之间的边界,必须对应到真正的测试边界。

第三,合并纪律变复杂。底层一旦改动,上面所有层都要 rebase。如果团队不及时同步,很容易出现「栈灾」。文章里已经提醒了这一点,现实中我也踩过类似的坑,分支树一旦乱掉,修起来比特么直接一个 PR 还麻烦。

第四,agent 目前还不会主动提出「要不我拆成 stacked PR 吧」。它需要你事先把结构写在 prompt 里,或者通过 skill 喂给它。也就是说,结构仍然是人类的责任,AI 只是执行者。

我的建议,今晚就可以试试

如果你是个人开发者,我觉得今天晚上就可以拿一个小功能试试。

先别贪大。找一个后端和前端分界清晰的 feature,比如加一个新 API 和它的展示页。先写清楚三层结构,数据层、API 层、UI 层。然后让 agent 一层一层做。你不需要告诉它每一行怎么写,你只需要告诉它每一层的边界和验收条件。

你会发现,自己不再是在一个巨大 diff 里找 bug,而是在每一层门口检查通行证。通行证对,就放行;通行证不对,就打回去重填。

如果你是团队,建议先从一个大家都不怎么抗拒的小 feature 开始跑,别一上来就拆十层。把第一层跑顺,比拆得漂亮更重要。等大家都适应了「栈」的节奏,再推广到更复杂的场景。

一个可以复制的小模板

如果你不知道怎么开口,可以把这个 prompt 直接扔给 Claude Code 这类 agent,让它自己拆。

1
2
3
4
5
6
我要给购物助手加一个产品搜索功能。请你按四层 stack 来做,每一层只改一个关注点。
L1 是数据层,包括类型、seed 数据、验证规则和数据访问模块。
L2 是 API 层,包括一个带校验的 `/api/products/search` 端点。
L3 是前端接线层,让聊天组件能调用真实 API 返回答案。
L4 是 UI 层,包括产品引用卡片、loading、空状态和错误状态。
每完成一层,先用 gh stack submit 推一个 PR,等通过验收后再做下一层。

你第一次可能不会拿到完美的四层,但至少 agent 会开始思考边界。多跑几次,它就能学会这种节奏。我试下来的感觉是,最难的其实不是让 agent 写代码,而是让它学会在每一层停下来,等人类点头。

最后

GitHub 这篇文章里最打动我的,不是 gh stack 这个工具,而是它承认了一个事实,AI 写代码的规模感,正在把代码审查从「个人经验」变成「系统工程」。

堆叠式 PR 不是让 AI 更像人,而是让人类 reviewer 用更像人的方式,去驾驭 AI 的产出。

所以回到开头那个 1721 行的 PR。现在我会先问自己,这 1721 行里,有多少是属于同一层的?如果它们本可以分开,那不如现在就拆开。把一次巨大的提交,拆成四个可以独立评审的小步。每一步都小,但合起来,反而更快。

来源参考,GitHub Blog — Turn one giant AI-generated pull request to a reviewable stack(https://github.blog/engineering/turn-one-giant-ai-generated-pull-request-to-a-reviewable-stack)