你两年前就禁掉了 componentWillMount,ESLint 也标记它,代码库里早就零使用。Cursor 生成一个新组件,悄悄又用了 componentWillMount。你指出 lint 报错,AI 说”好的我马上修”,改完那个文件。三个 prompt 之后,在另一个完全无关的组件里,componentWillMount 又冒出来了。AI 看得到 lint 规则,也表示同意,然后在跨轮次时直接忘掉。同样的剧情在 var、ESM 项目里的 require()、React 16 风格的 useEffect 清理逻辑、pg.Pool.connect 回调而非 Promise 上一遍遍上演。模式很清楚:AI 训练数据里仍然充斥着废弃形式,lint 反馈只在当前轮次有效,你的护栏没有触达真正需要的地方。
常见原因
按真实会话中出现频次排序。
1. AI 训练时见到的大量代码都还没废弃这个 API
GitHub 上公开代码里大部分还是旧写法。模型见过一百万次 componentWillMount,只见过一千次 componentDidMount,概率自然偏向旧的。
如何识别:在与你修过的位置毫无关联的其他文件里,同一个废弃写法反复出现。
2. lint 反馈只覆盖单次轮次
Cursor 或 Claude Code 读到 lint 输出,把那一行改掉,接着进入下一个任务——规则并没有进入长期记忆。
如何识别:同一条 lint 规则跨会话反复触发;“修复”只在同一对话内生效。
3. 项目层没有规则告诉 AI 什么是被禁的
.cursorrules、CLAUDE.md 或 system prompt 里压根没提到这条废弃。AI 没有任何信号说明这个代码库比公网更严格。
如何识别:查规则文件——如果废弃模式没出现在里面,AI 就完全没有理由避开它。
4. ESLint 把规则设为 “warn” 而不是 “error”
规则存在,但只 warn 不 error。AI 跑完 lint 看到”通过了”,其实是被警告淹没了。
如何识别:eslint --max-warnings 0 失败,但单跑 eslint 退出码是 0。
5. 废弃是项目自定义规则,AI 无从知晓
你用自定义规则禁了生产代码里的 console.log。AI 要等到跑 lint 才知道有这条规则,即便看到了也未必理解为什么禁。
如何识别:lint 输出里的规则名,在 AI 的解释里从来没被提到过。
6. 库升级后悄悄废弃了旧写法
你把 pg 从 v7 升到 v8,回调写法被废弃。AI 训练数据 v7 和 v8 各占一半,挑哪个全看”感觉”。
如何识别:npm ls <package> 显示是较新的主版本;废弃写法对应的是上一个主版本。
开始之前
- 把当前在意的所有废弃模式列清楚,写下来:“我们永远不用 X,总是用 Y”。
- 跑一遍
eslint --max-warnings 0,确认你的规则确实按 error 而非 warning 在执行。 - 看一下
.cursorrules/CLAUDE.md当前内容。 - 抓住一个 AI 重新引入废弃写法的实例,保存文件名和前后 prompt。
需要收集的信息
- 废弃模式(如
componentWillMount、var、require()、回调 API)。 - 替代写法(正确做法)。
- 应该触发的 lint 规则名(
react/no-deprecated、no-var等)。 - 该规则在
.eslintrc/eslint.config.js中的启用状态与严重度。 .cursorrules/CLAUDE.md/AGENTS.md的内容。- 如果废弃和库相关,库的版本号。
分步修复
按从快速见效到长期策略排序。
第 1 步:把 lint 规则改成硬性 error
eslint.config.js 或 .eslintrc 里:
{
"rules": {
"react/no-deprecated": "error",
"no-var": "error",
"import/no-commonjs": "error"
}
}
CI 强制零警告:
eslint . --max-warnings 0
这样 AI 那句”我跑了 lint,通过了”就真的会在废弃写法上失败。
第 2 步:在规则文件里写明禁止项
仓库根目录的 .cursorrules 或 CLAUDE.md:
## Banned patterns (do NOT generate)
- componentWillMount, componentWillReceiveProps, componentWillUpdate
→ use componentDidMount + useEffect equivalents
- var → use const, let
- require(), module.exports → use ESM import / export
- pg client callback API → use the promise / async-await form
- console.log in src/ outside of src/lib/logger.ts
If you generate any of the above, the lint check WILL fail.
Verify your code does not contain these patterns before responding.
这能把禁止项注入到 AI 每一轮的工作上下文里。
第 3 步:用正例告诉 AI 正确写法长什么样
加一份 docs/conventions.md,左右对照,然后在规则文件里引用它:
# Banned vs preferred
## React lifecycle
WRONG:
componentWillMount() { this.fetch(); }
RIGHT:
useEffect(() => { fetch(); }, []);
## Module system
WRONG:
const x = require("foo");
RIGHT:
import x from "foo";
具体对照比抽象禁令有效得多——AI 会模仿 RIGHT 那边的样式。
第 4 步:把 lint 放进 pre-commit hook
package.json:
{
"scripts": {
"lint": "eslint . --max-warnings 0"
},
"lint-staged": {
"*.{ts,tsx,js,jsx}": "eslint --max-warnings 0"
}
}
配 husky / lefthook 等 pre-commit hook。提交时的强制校验 AI 绕不过去。
第 5 步:能 --fix 的就自动修
很多废弃规则自带 autofix(no-var、prefer-const、prefer-arrow-callback 等):
eslint . --fix
每次 AI 生成完跑一遍,免去人工抓回归的认知负担。
第 6 步:大范围迁移用 codemod
回调转 Promise、class 转 hooks、CommonJS 转 ESM 这种大动作,用 jscodeshift 或 ts-morph:
npx jscodeshift -t ./codemods/cb-to-promise.js src/
codemod 跑完后,代码库里废弃写法的样本归零——AI 对你代码库做 RAG 时,只看得到新写法,自然会模仿新风格。
验证
eslint . --max-warnings 0在整个仓库零警告退出。- 让 AI 在 3 个不同 prompt 下生成新文件——没有一个再引入废弃写法。
grep -r "<deprecated-pattern>" src/全仓零命中。- 故意提交一段废弃写法,pre-commit hook 用清晰错误信息拦下。
长期预防
- 在
CLAUDE.md/.cursorrules里维护”deprecations”小节,每次升级主版本依赖时回顾。 - ESLint、TSC、自定义 AST linter 对废弃 API 统一设为 error——warning 是 AI 会忽略的训练噪声。
- 每条禁令都配上正确替代写法,不要光禁不教。AI 对”这样写”远比对”别那样写”敏感。
- 定期
grep -r "<deprecated>" src/清理漏网之鱼;AI 工具非常忠实地模仿仓库现状。 - 升级主版本时,专门拉一个 PR 用 codemod 把废弃 API 在整个仓库扫一遍。混合状态既迷惑人也迷惑 AI。
- 简要写下每条废弃的”为什么”。AI 对”componentWillMount 在并发模式下不安全”的锚定效果,远好于一条干巴巴的禁令。
常见误区
- 把规则停在 “warn” 级别。AI 看到 lint 通过就以为自己干完了。
- 禁令只写进自己的笔记,而不是 AI 真正会读的规则文件。
- 在一个文件里修了废弃写法,却没 grep 整个仓库——通常还埋着 5 到 10 处。
- 以为 AI 会从本次对话内的纠正里”学到”,于是不写规则文件。跨会话不持久,只对同一对话的后续轮次有效。
- 同一个文件里新旧风格混杂。AI 会挑离它最近的那种风格沿用下去。
相关问题见 AI 建议了过时的依赖、AI 引入的 TypeScript 错误、Claude Code 缺失项目上下文。
FAQ
Q:我一直在纠正它,它一直犯同样的错,是我哪里没做对吗?
纠正只在当前对话有效。把规则搬到 .cursorrules / CLAUDE.md,新会话才会继承。对话内提醒只是短期记忆。
Q:lint 规则已经启用了,AI 还是生成废弃写法,为什么?
AI 是”先生成 → 再跑 lint → 再修当前轮次的文件”的流程。如果你在 lint 跑之前就接受了代码,或之后才手动 lint,废弃形式就溜走了。把 lint 做成 pre-commit 硬卡口。
Q:我直接把废弃 API 从库里删掉行不行?
如果库由你掌控,这是最干净的做法。第三方库就不行了,只能靠规则文件 + lint 强制。
Q:怎么让 AI 主动推荐现代写法,而不是只是不用旧的?
每条禁令都配上一个具体的 RIGHT 示例。AI 模仿正面样本的能力远强于尊重抽象禁令。