Kevin Lynagh 这篇 4 月 20 日的技术随笔,不是一条行业新闻。它更像一份开发者自检报告。
他用三个个人项目说明一件老事:成功标准清楚,项目会收口;成功标准含糊,查资料、找 prior art、追求通用解,很快会把四小时任务拖成深井。
我更在意的不是哪款 structural diff 工具更好。真正的变化是:LLM 让写代码变快了,也让范围膨胀变便宜了。以前懒得写的鸡肋功能,现在模型能立刻生成。坏主意披上了“专业实现”的外衣。
成功标准越清楚,项目越容易做完
木工架子是正例。
Lynagh 的目标很窄:和朋友 Marcin 周末做点东西,适配自家厨房,用剩余木料,3D 打印宜家收纳盒挂件,再用朋友剩下的油漆处理边缘。周末结束,架子挂上墙。
这个项目没有被“更通用”“更优雅”“更完整”拖走。原因简单:验收标准够硬。能不能用,能不能装,周末能不能完成。
结构化 diff 项目就麻烦得多。
他发现 difftastic 在某个代码变更里,没有把 struct PendingClick 按自己的预期匹配起来。这个问题本来可以是一个小修小补,也可以变成一次工具生态巡游。结果他开始看 semantic diff、Tree-sitter、GumTree、SemanticDiff、diffsitter、mergiraf、weave、diffast 等工具。
四小时过去,原问题没有真正解决。倒是多了算法、论文、MCP server、工具边界和一堆新岔路。
| 项目 | 原始目标 | 被带偏的方向 | 更现实的判断 |
|---|---|---|---|
| 厨房架子 | 周末做完一个自用物件 | 基本没有扩范围 | 成功标准清楚,所以能交付 |
| Emacs diff 工作流 | 给自己做更舒服的代码审查视图 | 调研整片 structural diff 工具生态 | 调研有用,但容易替代行动 |
| Emacs 路径搜索 | 做 Finda 风格的全文件系统模糊搜索 | 被 Nucleo 的 anchor 功能带出额外索引设计 | 最后删掉多余代码,回到真实需求 |
这个对比很刺眼。
架子不是技术含量最高的项目,却完成了。diff 工作流更“专业”,却更容易滑进坑里。问题不在聪明不聪明,而在目标有没有边界。
对独立开发者来说,这会直接改变工作动作:调研前先写下验收句。比如“我只要在 Emacs 里更快 review LLM 输出”,不要写成“我要解决通用语义 diff”。前者是工具,后者可能是研究课题。
AI 没消灭 YAGNI,只是更快制造 YAGNI
Lynagh 的 LLM 编程案例更典型。
他想在 Emacs 里做一个类似 Finda 的路径模糊搜索。思路并不玄:遍历文件系统,收集路径,做三元组索引,用 bitmap 加速查询。
他让 LLM 进入 plan mode。模型推荐了 Helix 编辑器使用的 Nucleo 库。Nucleo 支持 smart case 和 Unicode normalization,例如 cafe 可以匹配 café。这些功能在模糊搜索里确实有价值。
麻烦出在 anchor。
Nucleo 支持 ^foo 只匹配行首。但文件路径通常从 / 开始,“行首”在这个语料里价值不大。于是作者试图把 anchor 改造成“路径片段开头”,又要处理 ^foo/bar,又要维护 segment boundary。
LLM 很配合。它可以讨论方案,也可以写包装代码。问题是,几小时后作者发现:自己根本不需要这个功能。
最后,删掉。
这就是 AI 编程的反直觉代价。它让试错便宜,也让分心便宜。以前一个多余功能会因为手写成本太高而死在脑子里;现在模型能把它写出来,还写得像正经工程。
YAGNI 在 AI 时代不是过时了,而是更要命了。You Aren't Gonna Need It 的重点从“别浪费手写时间”,变成“别让模型替你浪费判断力”。
“天下熙熙,皆为利来。”放在工具生态里,这个“利”未必是钱。也可能是 GitHub star、HN 讨论、功能表、插件入口、MCP server。功能越堆越满,看起来越专业,个人开发者的选择成本也越高。
这和铁路泡沫、互联网泡沫不完全一样。那是资本市场的过度兴奋,这里更多是注意力市场的过度供给。但重复的是同一种人性:基础设施真的有用,围绕基础设施长出的叙事也真的会吞掉判断力。
真正受影响的是会写代码、也会被工具带跑的人
这篇随笔对普通用户影响不大。它真正打中两类人。
一类是独立开发者和工具作者。你本来只想补一个工作流缺口,结果很容易把项目升级成通用平台、完整框架、可配置系统。听起来更体面,交付更遥远。
另一类是用 LLM 写代码、再自己 review 的程序员。你的瓶颈已经不只是“模型会不会写”。更现实的问题是:你能不能看懂 diff,能不能拒绝多余实现,能不能在模型兴致很高的时候按下停止键。
结构化 diff 这里要保持克制。
传统 line diff 只看行,容易看丢函数、类型、括号归属等代码结构。difftastic 用 Tree-sitter 的具体语法树改善这一点,但在实体匹配上不总符合人的预期。SemanticDiff 做得更产品化,有 VSCode 插件和 GitHub PR 网页视图,但作者没有找到可直接拿来做自己 Emacs 工作流的代码库。
GumTree 有学术根源。mergiraf 用 Rust 和 Tree-sitter 做 merge-driver。weave 也在往更复杂的方向堆功能。
这不能推出“某个工具失败”。目前只能说:语义 diff 很难,语言解析很脏,人的审查预期也不稳定。连 SemanticDiff 作者都曾在 HN 评论里提到,他们因为 Tree-sitter 在语义可靠性上的问题转向别的办法。
所以,开发者更该补的不是工具崇拜,而是止损规则。
| 场景 | 该做的动作 | 停止条件 |
|---|---|---|
| 想改善 LLM 代码审查 | 先改自己的 diff 视图和 review 流程 | 一旦开始设计通用 semantic diff,就降级回原需求 |
| 想接入新库 | 只接真实用到的功能 | 示例代码开始服务未来假设,就删 |
| 想调研 prior art | 限定时间盒,比如 2-4 小时 | 调研不能产出下一步实现,就暂停 |
| LLM 生成额外功能 | 默认要求解释当前需求为何需要它 | 解释不清,直接不合并 |
接下来最该看的不是哪个 diff 工具赢,而是你自己的工作流有没有三条线。
第一,原型时间盒。四小时没有接近可用,就收缩目标。
第二,功能白名单。只做当前任务需要的,不给“以后可能用”开绿灯。
第三,删除机制。LLM 写出的代码默认要接受审判,不是因为它能编译就该留下。
AI 让手更快,但项目能不能完成,仍然取决于人能不能收口。工具越强,边界越要硬。没有边界的专业感,只是更体面的拖延。
