既然 AI 都替你写代码了,还要 Lint 规则干什么?
几天前,我在公司合并了一个改动:把整个 monorepo 的 ESLint 换成了 oxlint。顺便说一句,oxlint 真的很棒。规则集完全对齐之后,一次全量本地 lint 从 3 分钟缩到 1 秒左右。那天心情相当不错。我终于可以告诉大家:以后 push 不用再加 --no-verify 了。这个粗暴的绕过手段,原本是为了跳过慢得要命的本地 lint 检查,结果几乎成了所有人的肌肉记忆。
我在 Slack 开了个帖子,向团队解释这个改动,还说我很期待这次速度提升,因为它能帮大家省下大量时间——尤其是像我这样已经转向「智能体工程」(agentic engineering)、基本不亲手碰代码的人。
一位同事问了个挺合理的问题,大意是:
既然你都不手写代码了,干嘛还在乎 lint 规则?
这个问题值得认真回答,因为到了 AI 辅助编程的时代,lint 的角色已经发生了根本性变化。
剧透一下:它比以往任何时候都更重要。
AI 之前,lint 是做什么的
从 lint 诞生起,它就是对下一个读这个文件的人的一种礼貌——那个人可能是你自己,也可能是别的贡献者。统一格式、别留无用导入、命名要这样而不是那样、别为了少写几行就硬凑一行代码——因为代码读起来应该轻松、可预期。说白了,就是把一份风格指南固化成可自动执行的规则,这样大家在复杂代码库里协作时才不会统统抓狂。
它还能抓出一类根本不算「风格」的问题。比如一个没有被正确 await 的 promise;一个 useEffect 的依赖数组漏了某项(React,爱你哟);一个声明了、赋值了、却从没被读取的变量。这些都不是风格问题,而是 bug,或者说正在变成 bug 的东西。我们可以自动把它们在引发故障之前抓出来。
当 AI 替我们写代码时,这两项工作——面向读者、抓 bug——依然存在。变化的只是:谁在写代码,谁在读代码。
作者变了,读者也变了
当我让编程智能体(AI agent)去处理一个 Jira 工单时,它写出来的代码会先被我看一遍,然后提交评审,由我的队友们审阅;万一几个月后系统出问题,可能还有倒霉蛋要再读一遍。这些读者依然存在,当初让人类读代码时成立的那些用 lint 的理由,放到 AI 时代也依然成立。
但现在多了一位 ✨新✨ 读者,它读代码的方式跟我们略有不同。在动手写一行代码之前,智能体会先读取周边文件,摸清这个代码库的行事风格。它这么做不是为了好玩,也不是为了烧 token,而是在寻找可以照抄的模式。
这就让代码库的整洁度产生了以前没有的复利效应。死代码和没用的 import 不只是碍眼——它们对智能体来说近乎毒药。如果代码库本身不干净、不统一,智能体就会把这些坏味道放大,到处传播坏模式。
如果你不认真维护一份 AGENTS.md 文件,把智能体该遵循的约定和模式写清楚,那 AI 就会继续捡起它能找到的一切——无论是好是坏——再传播出去。而且就算你维护了这份提示词文件,AI 也可能视而不见。提示词文件只能算是指导建议,不是真正能强制执行的规则,没人能给你打包票。
这时候就该老牌的 lint 规则登场救场了。你定义好规则,AI 就得像人类一样乖乖照做。这就是那份「能强制执行的保证」:违反规则的代码进不了你的代码库,因为 linter 会拦下 push。当然,前提是你搭好了靠谱的 CI/CD 流水线,在合并前强制执行 lint 检查——这是你应该做的!
代码评审成了紧俏的瓶颈
AI 在短时间内写出大量代码,工程团队里随之出现了一种新的稀缺资源:评审者的注意力。
在我跟工程师们的一对一沟通中,下面这些抱怨非常常见:
我很难及时拿到评审反馈。
感觉自己一天到晚在求人评审。
我写代码的速度,已经超过别人审我代码的速度了。
审查者真正擅长的事只有一件:判断意图。这个改动是不是满足了工单的要求?用的抽象层对不对?我们希望这个行为这样运作吗?让人类审查者去盯「29 行有个没用的变量」或者「这个 promise 没被使用」,纯属浪费时间。lint 把这些检查统统从审查者手里接过去,好让他们的注意力花在最有价值的地方。
要是你信了那套说辞,觉得 AI 写的代码反而不需要那么多自动化检查,那你就是在不知不觉中把这些活儿甩给同事去兜底。这个方向完全搞反了,结果就是你有 100 个待合并的 PR,却一个愿意审的人都没有。那不是瓶颈,那是塞子。
干脆连类型检查和测试也一起砍掉算了
没人会说「既然代码是 AI 写的,那还做什么自动化测试」。类型检查同理。你之所以要检查任何人写出的东西,是因为作者会犯错。AI 模型犯错的方式跟人不一样,而且可以说更难预测,这恰恰说明我们更需要确定性的验证手段,而不是更少。
关键结论: AI 写的代码并不天然比人写的更可靠,它只是产出更快。它需要更强的护栏——也就是 lint、类型检查和自动化测试,同时还需要有经验的工程师给出好的提示词。
在哪儿执行,取决于谁在提交代码
一旦你接受了 lint 很重要这个前提,下一个问题就是该在哪儿跑它。而这一点,我认为确实取决于你和你的团队怎么协作。
如果代码是人写的,一次提交就是一个存档点。你提交半成品,是为了去尝试一些有风险的做法,之后还能退回来。你可能带着一个没删的 console.log 就提交了,因为你正在调试,不想丢掉进度。一个会在 lint 报错时卡住的 pre-commit 钩子,会把每一个存档点都变成负担,而且这负担分散注意力的作用可能比带来的好处还大。对人来写代码这种情况,把检查卡在 pre-push 阶段大概才是对的。至于 CI 阶段,任何情况下都应该默认卡住,作为最后一道防线。
如果代码是智能体(agent)写的,一次提交就不是存档点,而是它在把结果回交给你之前、开发循环里的一个检查点。智能体不会因为提交时被 lint 错误拦住,就得鼓起勇气、分神去修它。它不会因为反复修一个没用到的 import 而心烦,也不会因为钩子失败就断了思路。它读到错误、改掉、再提交,通常几秒钟就搞定。失败出现得越早,这个循环就越短;循环越短,一条分支上积累的 lint 债务就越少。这样也就不需要专门来一次「清理 lint 错误」的提交,因为它一路都在每次提交时顺手做了。对智能体来写代码这种情况,我会说 pre-commit 才是合适的位置。如果你的 lint 跑得够快,还能更进一步,在智能体保存每个文件时就强制 lint,不过我觉得到那个粒度上收益会递减。
当团队里两种情况同时存在时,会出现一个尴尬的地方,而现在基本上所有团队都是如此。有些人在手写代码,有些人在指挥智能体,有时同一个人一天里两种都干。这种情况下,你就得把环境配成适合人来用。这也没什么大不了的,你顶多会发现自己的智能体大概率会跟随和人一样的模式:一条分支里最后一次提交是干净的,而中间那些提交可能都带着 lint 错误。
总之……
工程团队不该因为大家都转向完全靠 AI 写代码,就把 lint 规则丢掉。替我写代码的那个机器人,记忆力既不可靠又昂贵,品味谈不上多好,但对着 lint 报错却有无限的耐心——这恰恰让它成了 lint 最理想的读者。