前端工程经验如何沉淀为可执行规则
前端工程经验如何沉淀为可执行规则
线上事故复盘里,常见问题包括:组件卸载后没有清理微前端的全局事件监听、在 Vue3 计算属性中执行异步请求,以及 React Hooks 漏写依赖项造成闭包问题。
每次出问题都只要求“下次注意”,很难形成可执行、可复查的约束。AI 代码审查也不能只把源码交给模型:缺少项目上下文时,它可能盯着命名和格式,却漏掉闭包、资源清理等真正的风险。
大模型没有项目特有的上下文。要让 AI 审查发挥作用,需要把线上故障和 Code Review 的教训转成计算机能执行的决策规则。
1. 为什么传统的 Code Review 总是变成无休止的扯皮?
如果评审留言大多落在缩进、命名和引号偏好上,内存泄漏、竞态条件、无效重渲染等风险就容易被淹没。可以先统计一段时间内的 PR 留言分类,确认团队的注意力实际花在了哪里。
很多人以为买个 AI Code Review 工具就能解决问题。实际上,没有上下文约束的模型不知道微前端基座如何路由,也不知道封装的useRequest已自带防抖。直接问它“这段代码有没有 Bug?”,它只能按通用经验回答,甚至可能编造不存在的语法错误。
代码审查的核心不是评判“代码写得漂不漂亮”,而是拦截“与当前架构不符的危险动作”。只有把团队在特定业务场景下的失败经验沉淀为结构化的决策记录(Architecture Decision Record, ADR),AI 才能拥有精准的判断依据。
2. 故障转化的两层防线:AST 确定性规则与 Agent 语义规则
不是所有的经验都适合让 LLM 去判断。如果一个规则能用确定性的语法树(AST)检测出来,就绝对不要交给消耗 Token 且存在随机性的 AI。
我们在工程上把从故障中提取的规则分为两层:
- 确定性防线(AST 层):针对 API 误用、特定语法禁用、强制属性检查。例如:“在
components/business目录下避免直接 import 底层axios实例”。这种规则直接编译为自定义 ESLint 插件,在本地 Git Hook 阶段就应抹杀。 - 语义化防线(Agent 语义层):针对业务逻辑完备性、竞态控制、状态同步风险。例如:“在涉及到支付状态变更的自定义 Hook 中,应处理网络中断时的兜底状态”。这种规则需要依靠大模型理解上下文意图,由 Agent 在 PR 阶段扫描。
下面这套 TypeScript 实现的复盘规则提取与分发引擎,展现了我们如何将结构化的故障复盘记录转化为可执行的审查逻辑。
import * as parser from '@babel/parser'; import traverse from '@babel/traverse'; import { GoogleGenerativeAI } from '@google/generative-ai'; // 1. 结构化决策记录定义 (ADR) export interface IncidentRule { id: string; title: string; category: 'MEM_LEAK' | 'RACE_CONDITION' | 'SECURITY' | 'PERFORMANCE'; astPattern?: string; // 静态检测特征描述 semanticPrompt: string; // 提供给 LLM 的语义检查指令 severity: 'CRITICAL' | 'WARN'; } // 2. 复盘引擎实现 export class ReviewRuleEngine { private rules: IncidentRule[] = []; private ai: GoogleGenerativeAI; constructor(apiKey: string) { this.ai = new GoogleGenerativeAI(apiKey); } // 注册从故障中提炼的规则 public registerRule(rule: IncidentRule) { this.rules.push(rule); } // 第一层防线:确定性 AST 节点静态分析(以检测未清理的 EventListener 为例) public checkAST(code: string): { ruleId: string; line: number; message: string }[] { const violations: { ruleId: string; line: number; message: string }[] = []; try { const ast = parser.parse(code, { sourceType: 'module', plugins: ['typescript', 'jsx'], }); let hasAddEventListener = false; let hasRemoveEventListener = false; let addLine = 0; traverse(ast, { CallExpression(path) { const callee = path.node.callee; if ( callee.type === 'MemberExpression' && callee.property.type === 'Identifier' ) { if (callee.property.name === 'addEventListener') { hasAddEventListener = true; addLine = callee.property.loc?.start.line || 0; } if (callee.property.name === 'removeEventListener') { hasRemoveEventListener = true; } } }, }); if (hasAddEventListener && !hasRemoveEventListener) { violations.push({ ruleId: 'RULE-MEM-01', line: addLine, message: '检测到 addEventListener 但缺乏对应的 removeEventListener 清理逻辑,存在内存泄漏隐患。', }); } } catch (err) { console.error('AST 解析失败:', err); } return violations; } // 第二层防线:基于 LLM 的非确定性语义审查 public async checkSemantics(code: string): Promise<string[]> { const model = this.ai.getGenerativeModel({ model: 'gemini-1.5-pro' }); const semanticRules = this.rules.map(r => `- [${r.id}] ${r.title}: ${r.semanticPrompt}`).join('\n'); const prompt = ` 你是一名严格的前端代码审查专家。请根据以下团队历史故障提炼的审查规则,分析给出的代码是否存在隐患。 团队历史故障规则库: ${semanticRules} 被审查代码: \`\`\`typescript ${code} \`\`\` 输出要求: 1. 只输出命中的违规规则,格式为:[规则ID] 行号: 具体风险说明与修改建议。 2. 若无违规,仅输出 "PASSED"。 3. 严禁评价命名风格或格式等无关问题。 `; const response = await model.generateContent(prompt); const text = response.response.text(); return text.split('\n').filter(line => line.trim().length > 0); } }3. 把决策过程写入 Git 提交链路
规则引擎应嵌入开发者的日常流程,避免额外维护一个需要反复登录的后台。
我们的做法是直接把规则库放到项目根目录的.github/review-rules.json中。每次出现生产环境 P2 级以上的事故,责任人在完成 Bug 修复后,应提交一份包含规则更新的 PR。
在 CI 流水线中,通过 GitHub Actions 或 Git Hooks 触发审查脚本。如果静态 AST 阶段发现CRITICAL级错误,构建直接中断,并在 PR 评论区附上违规行号和对应的线上故障单链接。
开发者看到的不再是“格式不符合规范”,而是带有历史背景的提示:“上个月 15 号这里没处理竞态,导致用户重复扣款 5 万元,请参考 ADR-0815 加防抖处理”。
4. 效果评估与防线上移
效果应通过上线前后的 PR 平均通过时间、规则命中率和线上同类问题数来评估,并记录统计口径。
复盘中反复出现的风险可以被写成规则,新成员也能在提交阶段看到对应的背景和处理方式。此时 AI 只是审查链路的一环,规则本身仍应由团队持续维护。
能由编译期或 AST 检查确定的问题,应优先交给确定性规则;需要业务上下文的部分,再交给 AI 辅助判断。规则要能在日常提交流程中执行,也要随事故复盘持续更新。
