ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

用anti-slop规则集终结AI代码臃肿:Oxlint实战指南

用anti-slop规则集终结AI代码臃肿:Oxlint实战指南 “AI 写代码到底省不省时间”这是最近我和几个前端团队交流时最常听到的问题。大家的感受出奇一致代码量上去了测试也能过但 code review 越来越痛苦。打开一个 PR先要在十几行“看起来很合理”的代码里扒开重复的注释、绕圈的变量、伪防御式的 null 判断才能找到真正在做事的那几行逻辑。这种感觉已经不再是“代码写得差”而是代码里混入了一种特殊的坏味道——AI slop。anti-slop 就是针对这个问题出现的项目。它把“AI 生成的低质量代码模式”当作 lint 的目标专门为 Oxlint 定制一套有明确立场、有判断倾向的规则集。Oxlint 则是基于 Rust 实现的 JS/TS linter以惊人的处理速度著称。这两者结合传递的信号很明确在 AI 编程助手成为标配的今天我们需要的不是更温和的建议而是更快、更严格、更有立场的代码质量闸门。这篇文章会讲清楚三件事AI slop 到底是什么为什么反 AI 废代码这件事值得交给规则集来做以及如何把 anti-slop 风格的规则接入自己的项目并跑通“检查—发现问题—修复—验证”的完整闭环。如果你已经在用 AI 写业务代码或者正在为团队引入 AI 编程工具而担心代码质量失控这篇文章值得收藏。1. 为什么现在需要 anti-slop 这样的规则集1.1 传统 lint 解决不了 AI 时代的代码问题过去我们用的 lint 规则本质上是针对“人写代码的坏习惯”设计的。比如未使用的变量、隐式类型转换、缺少分号、魔法数字。这些规则有一个共同特点它们检查的是代码是否违反规范。但 AI 生成的代码问题恰恰不在这里。AI 代码的语法通常是对的类型可能也对测试也能跑通。问题出在代码的内容密度上——信息量很低存在感很强。举个例子一个函数签名叫getUserInfo(userId)AI 在函数体第一行加上// 获取用户信息。这句注释对吗对。有用吗没有它只是在复述代码本身。但传统 lint 不会报错因为注释不是语法错误。类似的情况还有把返回值先赋给一个临时变量再 return临时变量只用一次每个接口都套一层 try-catchcatch 里只打印日志然后返回 null一处逻辑被拆成五个函数美其名曰“复用”实际只有一处调用调用一个听起来很合理的 API实际上这个函数根本不存在。这类代码很难被“是否规范”的语言规则抓住因为它们本质上不是规范问题而是代码是否有存在意义的问题。1.2 团队引入 AI 编程助手后的真实痛点当一个团队开始广泛使用 AI 编程助手后仓库的 PR 数量通常会上升但每个 PR 的“信息密度”可能在下降。Reviewer 需要花费大量精力理解这段代码为什么这样写这个判断真的需要吗这个函数是业务必要还是 AI 为了“更像一段完整代码”而生成的装饰最麻烦的是这些问题很难通过“让 AI 更认真一点”来解决。AI 本身就倾向于生成平均数级别的代码它会把网络上最常见的写法缝合进来而网络上的最常见写法往往就是饱含各种防御和模板的保守写法。anti-slop 这类规则集的价值就在这里它把“代码品味”这个偏主观的东西转变成可以自动执行的客观检查项。哪怕这个检查项不能覆盖所有问题也至少能在 CI 阶段拦下一大批典型的 AI 废代码减少人工 review 的负担。2. AI slop 到底是什么2.1 从概念到代码“slop”这个词原意是泼洒出来的糊状物在网络语境里指那种“看起来像模像样、但没有真正营养”的生成内容。代码领域的 AI slop就是语法正确、逻辑能跑、但充满冗余与无意义表达的代码。它不是 bug不会让程序崩溃它也不是严格意义上的“坏味道”因为很多资深工程师也会写出防御性代码。它的典型特征是可以被识别、被归类的模式。下面这六个特征基本覆盖了 AI slop 最常见的形态。2.2 注释复述代码这是 AI 生成代码最典型的模式。函数名写清楚了逻辑注释又原封不动复述了一遍。// 获取用户信息 const userInfo getUserInfo(userId); // 遍历每一条订单 orders.forEach((order) { // 处理订单 handleOrder(order); });这类注释不会让代码更容易理解反而增加阅读噪声。真正有价值的注释应该解释“为什么这样写”而不是“这段代码在做什么”。2.3 多余的中间变量AI 常见做法是把一个表达式的结果先赋给变量再在没有改动的情况下使用这个变量。表面上看增强了可读性但如果变量名只是参数名的变形或者变量生命周期只有一行这就是纯冗余。const data getUserInfo(userId); const userInfo data; return userInfo;三行代码做了一件一行就能完成的事。2.4 伪防御式编程防御式编程本来是一种好习惯但 AI 常常把它做成形式主义的样板。到处判断 null却从不处理 null 的情况try-catch 捕获异常后只打一条日志然后返回 null 或默认值。function loadConfig() { try { const raw readConfigFile(); if (!raw) return null; return JSON.parse(raw); } catch (error) { console.log(error); return null; } }这段代码的问题在于catch 到错误后没有降级方案没有重新抛出没有错误上下文直接吞掉异常并返回 null。调用方拿到 null 之后同样不判断最后错误在更远的地方以更隐蔽的方式炸开。2.5 过度抽取函数与伪抽象AI 喜欢制造“看起来可复用”的函数。如果一个函数全仓库只有一个调用点并且函数内部只有一段业务逻辑那这个函数到底是抽象还是包装function mapOrderList(orders: Order[]) { return orders.map((order) order); }如果mapOrderList被四处使用有变化需求抽函数有意义。但如果它只在processOrders内部调用一次且没有独立的业务语义这就是一层无价值的壳。2.6 模板化样板代码在 API 层和业务服务层AI 特别容易生成千篇一律的模板统一的 try-catch、统一的日志、统一的返回结构。模板本身没有错错在把模板复制到每一个函数里却不根据业务调整异常处理和返回逻辑。这类代码的特点不是某一行有罪而是整段代码都在重复同样的模式且信息量趋近于零。3. 为什么偏偏选择 Oxlint3.1 Oxlint 是什么Oxlint 是 Oxc 生态中的 lint 工具由 Rust 编写目标是成为现代 JavaScript/TypeScript 工具链的高性能替代品。它兼容大量 ESLint 规则可以直接在大多数项目里替换 ESLint 的检查环节。从架构上看Oxlint 与 ESLint 最大的区别在于实现语言和并行能力。Rust 原生性能加上多线程让它在大型仓库上的处理速度非常可观。这不是说 ESLint 不好——ESLint 仍然是生态最成熟、插件最丰富的选择——但对于“规则数量越来越多、希望在本地和 CI 里都快速反馈”的场景Oxlint 更合适。3.2 anti-slop 与 Oxlint 气质匹配anti-slop 这类规则集有一个天然诉求规则可以很激进、很个人化、很多。因为“反 AI 废代码”不是把少数几个规则打开就完事而是需要覆盖大量模式。如果跑一次 lint 需要几十秒甚至几分钟开发者很快就会忽略检查结果。Oxlint 解决的正是这个问题。它在规则数量增加的前提下依然能保持较快的反馈速度。这对“让团队愿意在本地执行 lint”非常重要——工具再正确如果慢到让人不想用就等于零。还有一个层面是理念的契合。Oxlint 的官方定位里非常强调“默认推荐规则、减少配置负担、让检查结果可读”。anti-slop 则强调“有立场的规则集”。两者都反对那种“配置 500 条规则但全是摆设”的工程文化更倾向于让规则真正生效。3.3 也要清醒认识局限Oxlint 目前还不能 100% 平替 ESLint 的完整生态。如果你的项目深度依赖某个人气 ESLint 插件或者你有很多私有自定义规则迁移前必须做一次规则覆盖评估。更稳妥的策略是在传统 ESLint 流程保持不变的同时把 Oxlint 作为辅助检查接入 pre-commit 或 CI等规则覆盖确认后再逐步切换。对比维度ESLintOxlint实现语言JavaScriptRust处理速度规则增多后明显变慢并行处理速度优势明显规则生态最成熟插件丰富兼容大量 ESLint 规则生态仍在成长默认立场只启用极少数规则默认推荐规则更严格与 anti-slop 结合可用但反馈耗时更长更推荐适合高频执行4. anti-slop 规则集的核心规则类型需要先说明不同同类项目的规则命名可能不同这里整理的是 anti-slop 这类“反 AI 废代码”规则集通常会覆盖的模式类别。接入项目时具体规则名以项目 README 和配置文件为准。4.1 无意义注释与误导性注释这类规则检查注释是否为代码的简单复述以及注释是否与代码行为不一致。后者尤其危险很多 AI 生成的代码会先写注释再按注释生成代码但生成过程中逻辑已经变化注释还停在原地。触发场景// 把 id 转为字符串 return String(userId);“把 id 转为字符串”是String(userId)的逐字翻译没有增加任何信息。如果团队要求每个函数都带注释这种注释就会大量产生。规则的目的不是禁止注释而是禁止“假注释”。4.2 无意义别名与冗余变量检查变量是否在赋值后从未被修改且仅被使用一次并且这个别名没有起到澄清语义的作用。触发场景const result fetchData(); return result;这里的result没有比fetchData()提供更多信息直接return fetchData()更清晰。规则会把这类别名标记出来提示开发者判断别名是否真的帮读者建立了新的语义层次。4.3 空 catch 与吞异常检查 catch 块是否为空或者是否只包含一行日志而后没有任何降级、重新抛出、包装错误行为。吞异常是 AI 生成代码里最恶劣的模式之一因为它会让错误在系统里隐性传播。触发场景try { await syncData(); } catch (error) { console.error(error); }如果这里没有降级逻辑那么 catch 的唯一作用是“让程序不要崩”但数据同步失败的业务影响完全被隐藏了。这类规则通常会建议要么把异常包装后重新抛出要么在 catch 里实现明确降级要么至少给调用方一个失败信号。4.4 只使用一次的伪抽象检查只在一个位置被调用的函数是否真的需要作为独立函数存在。规则不会直接判定它必须被内联但会提醒开发者这个函数的抽象成本是否得到了复用收益。触发场景function getDisplayName(user: User) { return ${user.lastName} ${user.firstName}; } // 全仓库只有这一处调用 export function renderUser(user: User) { return p${getDisplayName(user)}/p; }如果getDisplayName确实只在这里使用且没有独立变化的需求更简洁的做法是直接内联。AI 特别容易生成这种“函数套函数”的结构来显得代码组织良好实际上是在增加跳转成本。4.5 冗余的模板样板检查重复出现的 try-catch-return 结构、重复的日志格式、冗余的 DTO 转换等。这类规则的实现通常需要模式匹配而不是单条语句。触发场景多个接口方法几乎共享同一个模板只有中间一行不同。规则会提示把公共部分抽成真正的工具函数而不是让模板代码铺满每一个函数体。4.6 注释掉的代码AI 在修改代码时有时会保留旧的实现片段用注释包裹起来“以防万一”。这类代码既影响阅读又会误导后来者。规则直接检查连续多行被注释的代码块识别出这不是说明性注释而是残留代码建议直接删除。5. 环境准备与快速接入5.1 安装 Oxlint在开始之前确保项目有 Node.js 环境并且已经初始化过 package.json。推荐使用 npm、yarn 或 pnpm 任一包管理器。npm install -D oxlintyarn add -D oxlintpnpm add -D oxlint安装完成后可以查看版本确认安装成功npx oxlint --version如果看到版本号输出说明 oxlint 已经可用。这一步值得先跑通再往下配置 anti-slop 规则。5.2 创建配置文件Oxlint 会自动读取.oxlintrc.json。如果你使用的是包管理器托管的配置文件也可以把它放在 package.json 的oxlint字段下。下面以独立的.oxlintrc.json为例展示如何引入一个“反 AI 废代码”风格的规则集。{ $schema: ./node_modules/oxlint/configuration_schema.json, plugins: [anti-slop], rules: { anti-slop/no-misleading-comments: error, anti-slop/no-useless-aliasing: warn, anti-slop/no-swallowed-errors: error, anti-slop/no-fake-abstraction: warn, anti-slop/no-commented-out-code: error } }这里用到的规则名是为了说明配置结构而写的演示名称。真实接入时应该以 anti-slop 项目实际发布的规则清单和插件名为准。配置思路是一致的plugins声明规则来源rules打开具体规则并设置错误级别。5.3 第一次运行先对单个文件跑一遍快速验证规则是否生效npx oxlint src/services/user.ts如果你希望检查整个项目npx oxlintOxlint 会输出检查到的错误和警告数量。第一次运行时如果项目里已经存在大量 AI 生成的代码你很可能看到成片的警告。这是好事说明规则集正在工作。6. 完整示例用 anti-slop 风格规则抓出 AI 坏代码6.1 一份典型的带“AI 味”代码下面这个src/services/user.ts文件集中了前面提到的好几种 AI slop 模式。把它放进项目再运行 anti-slop 规则就能直观感受检查过程。// 文件路径src/services/user.ts import { getUserInfo, getUserOrders } from ../api/user; // 获取用户信息 function fetchUserInfo(userId: number) { const data getUserInfo(userId); const userInfo data; return userInfo; } // 处理用户订单 function processUserOrders(userId: number) { try { const orders getUserOrders(userId); return orders.map((order) { // 遍历每一条订单 return order; }); } catch (error) { console.log(error); return null; } }这段代码从语法上看没有任何问题TypeScript 类型也正确。但读起来很累两个函数名已经说清了意图注释还在复述data到userInfo的赋值完全是多余的map没有做任何变换catch 块吞掉异常后返回 null调用方根本不知道发生了什么。6.2 运行检查npx oxlint src/services/user.ts预期输出类似src/services/user.ts 1:1 warning Comment only repeats code anti-slop/no-misleading-comments 4:3 warning Useless alias data anti-slop/no-useless-aliasing 7:1 warning Comment only repeats code anti-slop/no-misleading-comments 10:5 error Swallowed error with no fallback anti-slop/no-swallowed-errors 12:7 warning Useless map callback anti-slop/no-useless-callback 2 errors, 4 warnings输出格式因实际规则实现略有不同但重要的不是格式而是每一条提示都能指向一个真实问题。这个文件确实存在两种不同性质的瑕疵一类是纯粹的信息冗余另一类是错误处理逻辑的缺陷。6.3 修复代码修复的目标不是“消灭所有报错”而是让代码更简洁、更真实地表达业务意图。// 文件路径src/services/user.ts import { getUserInfo, getUserOrders } from ../api/user; export function fetchUserInfo(userId: number) { return getUserInfo(userId); } export function processUserOrders(userId: number) { const orders getUserOrders(userId); return orders; }改动说明删掉复述代码的注释因为函数名已经足够表达意图直接返回getUserInfo(userId)去掉冗余中间变量删除map的空操作直接返回orders把 try-catch 整个去掉。原来的 catch 块既没有降级方案也没有重新抛错只是一行console.log加一个null这比不捕获更危险。如果调用方需要捕获异常应该在更合适的位置做真正的错误处理而不是在每个函数内部悄悄吞掉。6.4 修复后验证再次运行npx oxlint src/services/user.ts预期输出src/services/user.ts 0 errors, 0 warnings代码行数从 22 行降到 10 行左右信息密度明显提升。这个例子虽然简单但它说明了 anti-slop 规则集的核心工作方式不是跟你讨论风格而是直接指出哪些代码没有存在意义。7. 运行结果与效果验证7.1 判断成功的标准npx oxlint的退出码为 0 时说明没有 error 级别的问题。CI 中通常把这条命令作为检查步骤如果退出码非 0则构建失败。Oxlint 默认会输出错误和警告数量一眼就能看出结果。实际项目中“全项目 0 error”不一定是最初就要达成的目标。尤其是存量代码库第一次接入时先保证没有 error、warning 逐步下降是更现实的节奏。7.2 接入 pre-commit 钩子为了让规则在本地提交阶段就生效可以使用 lint-staged 配合 husky。当开发者提交代码时只对被修改的文件执行检查避免全量检查拖慢日常开发。npm install -D husky lint-staged{ lint-staged: { *.{js,ts,tsx}: [npx oxlint] } }npx husky init在生成的.husky/pre-commit文件中加入npx lint-staged这个配置的含义是只有暂存区里新增或修改的 JS/TS 文件会被 Oxlint 检查。CI 里仍然可以保留全量检查形成“本地快速拦截 CI 兜底”的双闸门。7.3 接入 CI以 GitHub Actions 为例在.github/workflows/lint.yml中加入name: lint on: push: pull_request: jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 - uses: actions/setup-nodev4 with: node-version: 20 - run: npm ci - run: npx oxlint如果这一步在 CI 中失败第一步要看的不是具体某条规则而是失败时的退出码和第一条 error 输出。不要把整个 CI 日志从头翻到尾先定位第一个 error往往就是导致失败的原因。8. 常见问题与排查思路问题现象可能原因排查方式解决方案oxlint 命令找不到未安装依赖或 Node 版本过低运行npx oxlint --version查看输出重新安装oxlint检查 Node 版本anti-slop 插件不生效插件名或规则名写错查看启动日志确认插件加载情况对照项目 README 检查配置名和规则名现有代码库 suddenly 大量报错存量代码本身包含大量 AI slop 模式统计 error 与 warning 数量先只开 warning再逐步升级为 errorAI 助手改完后又引入同样的坏代码提示词没有把规则作为硬性约束查看 git diff 确认新增代码模式把规则清单写入团队提示词模板与 ESLint 报错重复项目同时存在两套 lint 流程对比两份检查报告迁移期双跑稳定后移除 ESLint 流程某个规则太激进误报很多规则默认严格度与团队阶段不匹配查看具体误报案例按目录关闭该规则或降级为 warning有一个排错原则值得记住遇到 lint 问题时先确认配置文件被正确加载再谈规则本身。很多“规则没生效”的情况其实是配置文件路径不对或者插件名写在plugins里但安装的包名不一致。对于 AI 反复引入坏代码的问题比较有效的做法是把检查结果转成团队提示词的一部分。你可以对 AI 助手说“请遵循项目中的 lint 规则不要在代码里添加与代码行为重复的注释不要吞掉异常不要生成只使用一次的抽象函数。”这虽然不能保证百分百生效但比什么约束都不给强得多。9. 最佳实践与工程建议9.1 分级启动不要一次性全量整改anti-slop 这类规则集的默认立场往往是“严格”的。如果直接把所有规则设为 error现有代码库可能瞬间出现几百个报错团队只能选择关闭规则或大改代码这两种极端都不是好结果。更合理的做法是第一期把规则设为 warning只收集数据不阻断提交。第二期只对新文件和新代码执行 error 级别检查。第三期逐步把存量代码纳入检查范围。这个过程会让团队有心理预期而不是把 lint 接入当成一次“代码大清洗”。9.2 把规则写进 AI 工作流如果你所在团队已经把 AI 编程助手当作基础设施lint 规则就不能仅仅停留在 CI 阶段。把规则的核心要求写进团队的 AI 提示词让 AI 在生成代码时就有“不要制造 slop”的意识效果会好很多。可以制作一个标准提示词片段例如项目规范 1. 不要添加与代码行为重复的注释。 2. 不要创建只使用一次的中间变量。 3. 不要吞掉异常catch 中必须包含降级或重新抛出。 4. 不要生成仅有一个调用点的伪抽象函数。 5. 不要调用不存在或不明确的 API。AI 生成代码后再通过 Oxlint 校验形成一个“提示词预防 lint 检测兜底”的组合。9.3 规则要可解释不要用工具压人lint 规则最容易引起的冲突是团队内部的“规则争议”。很多人看到一条奇怪规则的第一反应是“这条没道理”。这很正常因为“代码是否有必要存在”本身就是一道价值判断题。处理这类分歧建议用具体代码示例沟通而不是只给对方看规则文档。拿出修复前和修复后的代码对比阅读成本往往比争论“AI 不该这么写”更有说服力。规则集的目的是降低团队整体理解成本而不是制造新的教条。9.4 关注高价值规则而不是规则数量anti-slop 的核心价值不在“规则多”而在“规则准”。一个团队最值得优先落地的规则通常集中在吞异常、重复注释、伪抽象这三类因为它们直接决定代码是否好维护。至于某些风格层面的规则优先级可以放低。接入后建议每两周复盘一次规则报告看看哪条规则高频触发、哪条规则一直没人踩。没人踩的规则不一定没用但高频触发的规则一定值得关注它说明团队确实存在某种系统性的编码习惯。10. 总结与后续实践路径anti-slop 真正解决的问题不是“AI 生成的代码是错的”而是“AI 生成的代码常常没有存在的意义”。它把原来只能靠 reviewer 口头提的“这代码太啰嗦了”“这注释等于没说”“这个异常吞得没道理”变成了一套可以被自动执行的规则。Oxlint 则提供了足够快的执行底座让这套严格规则不至于拖累日常开发和 CI 流程。如果你准备尝试建议从一个小模块开始而不是直接接入整个仓库。选一个最近由 AI 助手生成的业务文件安装 Oxlint按规则集跑一遍把报错逐条修复。这个过程中你大概率会重新认识自己项目的代码质量基线。下一步值得继续深入的方向包括Oxlint 与 ESLint 的规则迁移对照、团队 AI 提示词与 lint 规则的联动设计、以及如何基于报告数据制定更长期的代码质量改进计划。工具只是入口真正有价值的是把“可维护性”从一句口号变成团队每天都能感知到的约束。
返回列表