随笔·阅读约 3 分钟·
AI 辅助编程的真实边界:从一次解析器 bug 修复看 AI 能做什么、不能做什么

AI 辅助编程的真实边界:从一次解析器 bug 修复看 AI 能做什么、不能做什么

htmx 作者 Carson Gross 用真实案例告诉你:AI 擅长调查根因和写测试,但在架构决策上仍需人的深度理解。别做只懂'Vibe Coding'的学徒。

原文来源:htmx.org — htmx 作者 Carson Gross 用一次真实的 bug 修复经历,剖析了 AI 辅助编程中哪些环节真正高效、哪些环节需要人类的深度介入。

引言:一个对 AI 持怀疑态度的人

Carson Gross(htmx 和 Hyperscript 的作者)从不掩饰他对 AI 的复杂态度。在之前的文章《The Sorcerer's Apprentice Problem》(魔法师的学徒问题)中,他警告过开发者过度依赖 AI 的风险:你会变成那个学徒,不断召唤 AI 帮你完成任务,却越来越不理解自己的系统是如何运作的。 一旦 AI 出错或不可用,你就束手无策。

但 Gross 也承认,完全拒绝 AI 也不是明智的选择。他决定用一个真实的 bug 修复经历来检验 AI 的价值——不是玩具项目,不是 LeetCode 题,而是 Hyperscript 解析器中一个真实的回归 bug。这篇文章就是那次实验的完整记录。

—— 广告 ——

背景:Hyperscript 的"非主流"解析器

Hyperscript 是一门为 Web 设计的解释型脚本语言,用 JavaScript 实现。它的解析器设计并非主流——它有意打破了传统解析的规则:

  • 解析逻辑分散在 parse 元素上,而非集中在一个大解析器里
  • 解析器是可插拔的,语法是在运行时动态定义的
  • 支持多种属性访问语法,这在解析时带来了额外复杂性

Gross 自己承认:"这不是我会推荐给大多数编程语言的方法,但对这个项目来说效果不错。"

然而,这种灵活性也意味着解析器的行为高度依赖上下文——而这恰恰是 bug 的温床。

Bug:fetch 命令中的 as 关键字冲突

用户在升级到 v0.9.91 后报告了一个回归 bug。以下表达式解析失败了:

code
fetch `{% url 'trade:get_symbol_data' %}?symbol=${symbol}` as JSON

这个表达式的意图很清晰:fetch 一个 URL,然后将响应结果作为 JSON 处理。但解析器报错了——它无法正确解析这个结构。

根因是什么? 问题出在 as 关键字身上。在 Hyperscript 中,as 有两个不同的角色:

  1. 在表达式中:类型转换——set x to "42" as Int(将字符串"42"转换为整数)
  2. 在 fetch 命令中:响应格式修饰符——fetch https://hyperscript.org as Text(以文本形式获取结果)

v0.9.91 版本中,Gross 重构了 go 命令,提取了一个公共方法 parseURLOrExpression(),并让 fetch 也共享了这个方法。这个重构的副作用是:fetch 后面意外支持了通用的"表达式"语法,导致解析器把 as JSON 理解成了类型转换的一部分,而不是 fetch 的修饰符。

AI 的优势:根因调查和测试生成

Gross 使用了 Claude 来定位这个 bug。他的评价很直接:

"这是我通常会依赖 AI 来帮助的领域。"

Claude 的表现

Claude 很快定位到了根因:

  • 识别出 v0.9.91 中 go 命令的重构引入了 parseURLOrExpression() 方法
  • 指出这个方法意外扩展了 fetch 后面的语法,使其支持了通用表达式
  • 解释了为什么 as 被错误地解析为类型转换而非 fetch 修饰符

这个调查过程如果纯靠人工,需要跟踪代码路径、理解重构变更、以及 Hyperscript 解析器的上下文敏感设计。AI 在快速浏览代码库、识别变更模式方面展现了明显的优势。

测试生成

Claude 还生成了针对各种情况的测试用例。Gross 评论说:

"这是另一个 AI 表现良好的领域。"

AI 生成的测试覆盖了:

  • 原始的失败案例(带模板字符串的 fetch)
  • fetch $url as JSON 变量形式
  • 不带 as 的普通 fetch
  • 其他边界情况

这些测试在后续验证修复方案时发挥了关键作用。

AI 的劣势:修复方案的质量

Gross 坦率地承认,他在找到根因后"有点懒",直接让 AI 提出修复方案。结果并不理想。

方案 1:一个 Hack

code
return this.parseElement("stringLike") || this.requireElement("expression");

这个方案的问题很明显:它只解决了报告中的具体案例。 如果用 fetch $url as JSON(变量形式的 URL),这个 hack 仍然会失败。Gross 形容它为"一个 hack"——不够通用,不优雅。

方案 2:更好的方案,但引入了不必要的复杂度

code
// AsExpression.parse()
if (parser.noConversions) return;

这个方案在解析器上加了一个 noConversions 标志,让 as 在某些上下文中不触发类型转换逻辑。这本质上是一个上下文敏感的标志——而 Hyperscript 的解析器已经是上下文敏感的了。

问题在于:AI 忽略了已经存在的上下文敏感机制——"follows"(追随标记)。

方案 3:接近了,但太宽泛

在 Gross 提示 AI 使用 "follows" 机制后,Claude 在 parseURLOrExpression() 中实现了 follow 技术。这解决了通用问题,但因为 fetchgo 共享同一个方法,修复也变得过于宽泛——它阻止了 go 命令中合法的 as 转换表达式。

最终修复:半有机演进的正确方案

最终,Gross 自己动手,将修复范围精确限定在 fetch 命令中:

code
parser.pushFollow("as");
try {
  var url = parser.parseURLOrExpression();
} finally {
  parser.popFollow();
}
 
if (parser.matchToken("as")) {
    // 这里是 fetch 的 as 修饰符处理逻辑
    ...
}

这个修复的精妙之处在于:

  1. 精准定位:只在 fetch 的解析作用域内推送 as 作为 follow
  2. 不影响 gogo 命令不受影响,仍然可以正常使用 as 转换
  3. 利用现有基础设施:没有新增标志或 hack,完全利用了 "follows" 机制
  4. try/finally 保证安全:即使解析出错,也能正确弹出 follow

Gross 指出,这个修复的 core insight(核心洞见) 来自他对代码库的深入理解——他知道 "follows" 机制的存在,理解它的工作方式,并且知道如何精准地应用它。AI 无法替代这种对系统的深层理解

核心教训:AI 是工具,不是替代品

1. "Vibe Coding" 的危险性

"Vibe Coding"(氛围编程)是 Andrej Karpathy 提出的概念——让 AI 写大部分代码,人只是"感受"一下氛围,大致看看结果。Gross 用自己的经历论证了这种做法的危险:

如果你不理解 AI 生成的代码,你就无法判断它是否引入了技术债务。

技术债务在 AI 辅助编程中特别容易积累,因为:

  • AI 倾向于生成"能用的"代码,而非"优雅的"代码
  • AI 不了解系统的历史演进和设计约束
  • 多个 AI 生成的拼凑在一起,整体复杂度会指数级增长
  • 每接受一个 suboptimal 的修复,就欠下一笔技术债

2. 黄金组合:资深开发者 + AI

Gross 总结的模式是:

阶段谁来做效果
调查根因AI快速、全面
生成测试AI高效、覆盖好
设计修复方案人类需要系统理解
实现修复人类 + AI 辅助人做最终决策
代码审查人类把关技术债

3. 减少技术债务至关重要

Gross 特别强调:技术债务是指数增长的。 一个 hack 会催生更多的 hack,一个临时的标志会导致更多的标志。对于资深开发者来说,减少技术债务比实现功能更重要——因为技术债务会直接拖慢未来的开发速度。

4. 50 岁后的观察

Gross 分享了一个个人观察:随着年龄增长(他 50 多岁了),记忆力和精力自然会下降。AI 在这方面提供了有价值的帮助——它可以快速检索代码、回忆上下文、生成样板代码。这弥补了自然衰老带来的认知变化。

但这并不意味着可以放弃对代码的理解。AI 是拐杖,不是替身。

总结:AI 能做什么、不能做什么

AI 能做的 ✅

  • 快速调查和根因分析:在大型代码库中追踪变更链
  • 生成测试用例:覆盖边界情况
  • 回忆代码上下文:辅助记忆力
  • 生成样板代码:提高效率
  • 辅助调试:提供可能的根因

AI 不能做的 ❌

  • 理解系统的设计哲学:不知道为什么某些设计决策被做出
  • 做出架构决策:无法评估长期维护成本
  • 避免引入技术债务:倾向于"能用就行"
  • 替代对代码库的深层理解:不了解历史演进和隐含约束
  • 做出优雅的修复:缺乏对代码库"感觉"

写在最后

Carson Gross 用一次真实的 bug 修复经历,为我们呈现了 AI 辅助编程的完整图景。这不是一篇鼓吹 AI 或唱衰 AI 的文章,而是一个务实的评估:AI 在某些环节极其高效(调查、测试),但在另一些环节(架构决策、优雅修复)仍然需要人类的深度介入。

对于开发者来说,正确的姿势不是拥抱或拒绝 AI,而是理解 AI 的能力边界,在自己的薄弱环节(记忆、调试、测试生成)使用 AI,在核心环节(架构、设计、代码质量)保持人类的判断力。

正如 Gross 所说:"AI 可以帮你找到问题、帮你验证方案,但真正理解你的系统,仍然是你自己的责任。"


原文:Working With AI: A Concrete Example — Carson Gross,2026年6月29日

分享到
微博Twitter

© 2026 四月 · CC BY-NC-SA 4.0

原文链接:https://www.aprilzz.com/ramble/working-with-ai-concrete-example