乐于分享
好东西不私藏

AI 写代码这件事(四):Diff 是信任的界面

AI 写代码这件事(四):Diff 是信任的界面
AI 修改代码的速度快得令人不安。Diff 的质量决定了你是在做有意义的审查,还是在走过场点 Accept All。
AI 能不能写好代码,这个问题大家都在讨论。AI 写完之后你能不能看清楚它做了什么,这个问题几乎没人讨论。但后者对你的代码质量影响更直接——当你不确定 AI 的修改是否正确、无法快速区分改动范围时,你要么不信任它(效率为零),要么全部 Accept(把自己变成橡皮图章)。不同工具对这个问题的设计哲学完全不同:Cursor 把 diff 做成了 IDE 的主要交互中心;Claude Code 依赖 git 作为真相来源;opencode 把执行过程全程可见。哪种更好用,取决于你的工作方式。

两种审查路径的本质差异在操作对象的粒度:Cursor 的 inline diff 让你针对每一块改动做 accept/reject 决策;Claude Code 的路径是改完所有,你用 git 做整体审查。前者慢但精细,后者快但要求你善用 git diff 和 git add -p


为什么 diff 是信任的界面

人和 AI 的协作里有一个不可回避的不对等:AI 生成代码的速度远快于人类阅读的速度。

这个不对等是好事——这就是为什么 AI 工具有价值。但它同时带来一个问题:当你不想全信 AI 的时候,你用什么工具来构建你自己的判断?

答案是 diff。

diff 是 AI 行为的界面——它把"AI 做了什么"以最可审查的形式呈现给你。好的 diff 界面让你在 30 秒内判断"这批修改方向对了,细节我再看";差的 diff 界面(或者没有 diff)让你只能选择全信或全不信。

Accept All 是一个危险的按钮,不是因为 AI 写的代码一定错,而是因为它训练你放弃判断。用久了,你的代码审查能力会退化到"这是 AI 写的,应该没问题"——这是个陷阱。


Cursor 的 inline diff:把审查做进编辑器

Cursor 的 diff 界面是这几个工具里最精细的,这是有意的设计选择。

当 Cursor Composer 完成一个多文件修改,每个改动块都以内联红绿 diff 的形式嵌入编辑器:

- const user = await getUser(id)+ const user = await getUserById(id)+ if (!user) throw new ApiError(404, 'User not found')

你可以:

  • Accept(×1)
    :接受这一块改动
  • Reject(×1)
    :回滚这一块,保留其他
  • Accept All
    :接受这个文件的全部改动
  • 或者直接在 diff 上方编辑——你可以把 AI 的建议改成你想要的样子,再 accept

这个设计来自一个假设:你应该看每一块改动。Cursor 把 diff 放在你不可能忽视的位置——就在代码里,不是弹窗,不是侧边栏。

代价是:如果 AI 修改了 20 个文件,300 块 diff,你要做 300 次决策——这在大任务里会让审查变成体力劳动。这也是为什么很多人最终还是点了 Accept All。


Claude Code 的路径:git 是最终真相来源

Claude Code 不在 IDE 里做 diff——它直接修改文件,然后让你用 git 做审查。

它的假设是:你应该会用 git

# Claude Code 跑完之后$ git diff                    # 看所有改动$ git diff src/auth/login.ts  # 看某个文件$ git add -p                  # 交互式选择哪些改动进入 commit$ git checkout src/bad-file.ts # 撤销某个文件的全部改动

git add -p(patch 模式)是这个工作流里最重要的工具:它会把每一块改动(hunk)拆开,问你"这块要加进去么"。效果和 Cursor 的 inline diff 非常类似,只是发生在终端而不是 IDE。

这个路径有两个实际优势:

  1. git 是你已有的工具
    :不需要学新界面,撤销机制是整个编程生态里最成熟的
  2. 审查和提交是同一个操作
    :你用 git add -p 审查,同时完成了 staging——审查结果直接进入 commit 历史,可追溯

劣势是对 git diff 的要求较高。如果你不习惯在终端里做代码审查,这个工作流会比 IDE 的 inline diff 更费力。


opencode 的可观测性:让过程本身成为日志

opencode 的设计选择在这里非常有趣:它侧重于让执行过程本身可见,而不只是最终结果的 diff。

在 opencode 的 TUI 里,你实时看到:

  • AI 读取了哪个文件
  • 它写入了什么内容(执行前显示)
  • 命令的输出是什么

这不是传统意义上的 diff 界面,而是一种过程级可观测性——你不是在事后审查结果,而是在过程中保持低成本的感知。

对于信任度不高的大型操作,这个方式比事后 diff 更安全:你在中途就能判断"不对劲,停下",而不是等全部跑完发现方向就错了。


Copilot 的 inline chat:最轻的审查路径

GitHub Copilot 的 inline chat(Ctrl+I 触发)对应的是最轻量的场景:选中一段代码,让 AI 修改,AI 的建议以窗格形式显示,你 Accept 或 Discard。

这个路径适合改动范围在视野内的任务——你看得到全部改动,一眼就能评估。

Copilot Workspace 和 Agent 模式的 diff 体验比 inline chat 差不少:多文件改动的展示比 Cursor 更粗糙,审查工作流没有 Cursor 那么流畅。这是 Copilot 在 Agent 能力上比 Cursor 晚起步带来的产品债。


你需要的习惯,不只是工具

diff 界面设计得再好,也需要你配合两个习惯:

习惯一:在任务开始前 commit 一次

git commit -am "checkpoint before AI changes"

这一行是你的安全网。无论 AI 做了什么,你随时可以 git reset --hard HEAD~1 回到起点。没有这一行,你的撤销空间取决于 AI 工具的 undo 实现——这是一个弱得多的保证。

习惯二:不要 Accept All 大型任务

对于几行代码的小改:Accept All,完全合理。对于跨 10 个文件的重构:至少 git diff 过一遍再 commit。不是因为 AI 一定错了,而是因为你对自己的代码库负责,这责任无法外包给 AI。


延伸阅读

  • git add -p: The best way to review your changes — 官方文档,--patch 模式详解
  • Cursor: Understanding diffs in Composer — Cursor 的 apply 和 diff 界面设计说明
  • Anthropic: Claude Code best practices — 包含 checkpoint commit 的工作流最佳实践