
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。以下表达式解析失败了:
fetch `{% url 'trade:get_symbol_data' %}?symbol=${symbol}` as JSON这个表达式的意图很清晰:fetch 一个 URL,然后将响应结果作为 JSON 处理。但解析器报错了——它无法正确解析这个结构。
根因是什么? 问题出在 as 关键字身上。在 Hyperscript 中,as 有两个不同的角色:
- 在表达式中:类型转换——
set x to "42" as Int(将字符串"42"转换为整数) - 在 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
return this.parseElement("stringLike") || this.requireElement("expression");这个方案的问题很明显:它只解决了报告中的具体案例。 如果用 fetch $url as JSON(变量形式的 URL),这个 hack 仍然会失败。Gross 形容它为"一个 hack"——不够通用,不优雅。
方案 2:更好的方案,但引入了不必要的复杂度
// AsExpression.parse()
if (parser.noConversions) return;这个方案在解析器上加了一个 noConversions 标志,让 as 在某些上下文中不触发类型转换逻辑。这本质上是一个上下文敏感的标志——而 Hyperscript 的解析器已经是上下文敏感的了。
问题在于:AI 忽略了已经存在的上下文敏感机制——"follows"(追随标记)。
方案 3:接近了,但太宽泛
在 Gross 提示 AI 使用 "follows" 机制后,Claude 在 parseURLOrExpression() 中实现了 follow 技术。这解决了通用问题,但因为 fetch 和 go 共享同一个方法,修复也变得过于宽泛——它阻止了 go 命令中合法的 as 转换表达式。
最终修复:半有机演进的正确方案
最终,Gross 自己动手,将修复范围精确限定在 fetch 命令中:
parser.pushFollow("as");
try {
var url = parser.parseURLOrExpression();
} finally {
parser.popFollow();
}
if (parser.matchToken("as")) {
// 这里是 fetch 的 as 修饰符处理逻辑
...
}这个修复的精妙之处在于:
- 精准定位:只在
fetch的解析作用域内推送as作为 follow - 不影响
go:go命令不受影响,仍然可以正常使用as转换 - 利用现有基础设施:没有新增标志或 hack,完全利用了 "follows" 机制
- 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日
© 2026 四月 · CC BY-NC-SA 4.0
原文链接:https://www.aprilzz.com/ramble/working-with-ai-concrete-example