anti-slop 这个名字第一次看到的时候,很容易以为是一组“看代码不顺眼就想管一管”的任性规则。实际用过之后,我更愿意把它理解成:一套有明确价值取向的 Oxlint 规则集合,目标不是让代码“能跑”,而是让代码不脏、不慌、不至于在三个月后没人敢改。如果你正在负责前端工程的 lint 配置,或者刚从 ESLint 迁移到 Oxlint,又或者团队里有大量 AI 生成代码需要收口,那么 anti-slop 这套思路值得你先搞明白。它真正最值钱的地方不在规则数量,而在于把“规范”从可选项变成了门禁。
从标题里的两个关键词来看,anti-slop 描述的是约束目标,Oxlint 描述的是执行载体。它想解决的是很多工程团队都会遇到的真实问题:代码没有明显报错,但处处都是隐患。今天这篇就按实际落地顺序拆一遍:先讲概念,再讲环境,然后讲规则怎么配、批量任务和 CI 怎么接,最后把常见的坑和排查顺序一并说清楚。
1. 先搞清楚 anti-slop 管的是哪一类问题
1.1 SLOP 并不是脏话,它是代码里常见的偷懒模式
SLOP 这个说法在程序员圈子里流行起来,和 AI 生成代码变得普遍有很大关系。它并不是指某种具体技术,而是描述一类“看起来能用、细看全是毛病”的代码。典型的 SLOP 包括:先把逻辑堆上去再说的垃圾注释、到处留着的调试日志、为绕过类型报错随手写的 any、空 catch 块、函数越来越长、分支逻辑不断叠加、复制粘贴后只改了一半的重复代码。
这些代码不会立刻让项目崩掉,但会持续消耗维护者的精力。你以为是业务逻辑复杂,结果回头一看,很多复杂度根本不是业务要求的,而是写代码的人偷懒留下的。anti-slop 的立场很直接:这些偷懒模式不应该只靠 code review 时靠眼睛发现,应该用规则在提交之前就挡住。
所以 anti-slop 的前提是:你已经承认规则是有必要的。如果你觉得 lint 只是在制造麻烦,那这套思路你会越看越别扭。它不是为了让你开心的工具,而是为了减少未来踩坑概率的约束。
1.2 anti-slop 的价值不在规则数量,在“有观点”
Oxlint 本身已经内置了很多规则,ESLint 生态里也有几千条规则。anti-slop 如果只是把规则数量堆上去,意义不大。它真正值得注意的是“有观点”三个字。
普通 lint 配置往往分两类。一类是只开官方推荐,追求兼容性,规则能跑就行;另一类是规则开放得非常多,每个文件打开都是满屏警告,团队成员很快产生“lint 疲劳”。anti-slop 的思路不太一样:它明确告诉你哪些代码模式是不被接受的,并且默认用 error 级别去拦。它不试图讨好所有人,也不追求“一条规则都不误报”。
我理解这种“有观点”体现在几个判断上:
- 代码不是只要正确就行,还要不能被误读。
- 代码不是只要简洁就行,还要能稳定维护。
- 代码不是只要通过类型检查就行,还要经得起三个月后陌生人阅读。
- 规则不是用来提建议的,是用来做门禁的。
一旦你接受这些判断,再去看 anti-slop 规则集,就会发现它的每一项都不是随机选的,而是围绕“减少维护成本”这个目标。
1.3 为什么选用 Oxlint 作为这套规则的落点
既然要搞一套严格规则,为什么不用大家更熟悉的 ESLint?原因可以拆成几点。
第一是性能。Oxlint 基于 Rust 生态的 Oxc 工具链实现,核心卖点就是快。对于大型前端仓库,全量 lint 的时间如果从几十秒降到几秒,开发体验会完全不一样。严格规则可能会带来很多新警告,如果工具本身跑得慢,开发者更不愿意在本地跑,规则就变成了摆设。
第二是配置简单。Oxlint 的定位是“开箱即用”,默认就会启用一些高价值规则。它不需要像 ESLint 那样先装解析器、插件、共享配置,再处理各种版本冲突。对于刚从零开始建仓库的团队,这个优势很明显。
第三是它和 ESLint 的关系不是替代,而是互补。Oxlint 支持大量与 ESLint 兼容的规则语义,这让团队在迁移时不用把配置文件推倒重来。你可以先用 Oxlint 做快速门禁,再把更细的检查留给 ESLint 或者 TypeScript 类型检查。
当然,并不是说 Oxlint 适合所有项目。规则兼容性、插件生态、团队习惯这些因素仍然要评估。但如果你想做一套严格且能跑进 CI 的规则,Oxlint 是个很合适的切入点。
2. 落地之前,先把环境和配置准备好
2.1 安装和前置条件:能跑通是第一步
无论你用的是 npm、pnpm 还是 yarn,安装 Oxlint 都不复杂。最常见的方式是在项目中安装局部依赖:
npm install -D oxlint然后在 package.json 里加一个脚本:
{ "scripts": { "lint": "oxlint" } }如果你只是想临时试一下,不想改 package.json,也可以用 npx:
npx oxlint src我一般会先跑一个具体的输入目录,比如src或lib,而不是直接对整个项目根目录跑。原因很简单:项目根目录里经常有配置文件、构建产物、第三方代码,这些目录会被忽略还好,一旦没有被正确忽略,输出里全是不该看的警告,反而浪费时间。
网络环境不需要额外担心,只要你的包管理器源正常就行。Node 版本建议用当前团队推荐的长期维护版。这里给不了绝对版本号,因为不同版本的 Oxlint 对 Node 版本要求会有变化,落地时以你安装到的版本说明为准。
如果你已经装了 Rust 工具链,想直接从源码跑也可以,但通常情况下没必要。用 npm 包是最省事的方式。
2.2 配置文件放在哪里,基本字段怎么理解
Oxlint 的配置文件可以使用 JSON 格式。是不是标准名为.oxlintrc.json,要看当前版本,但整个配置结构是按规则集加参数组织的。一个最基础的示例长这样:
{ "rules": { "eqeqeq": "error", "no-var": "error", "no-empty": "error" } }这只是一个演示,别直接当成 anti-slop 的标准配置。更关键的是理解三个概念:
第一个是规则级别。off表示关闭,warn表示警告,error表示报错。anti-slop 的核心做法是让真正重要的规则保持error,否则无法形成门禁。警告往往会被忽略,一屏警告里混入一条真错误,人也会麻木。
第二个是忽略路径。配置文件里一定要有忽略规则,否则node_modules、dist、coverage这些目录会混进输出。你可以显式配置,也可以依赖默认行为,但落地时建议检查一遍。
第三个是环境。不同项目可能有浏览器环境、Node 环境、测试环境。规则集和全局变量定义要和这些环境匹配,否则会误报console、window、process之类标识符。
2.3 第一次运行,先看三样东西
配置好之后,不要急着把规则加满。第一次运行 Oxlint,我建议只做三件事。
第一,确认命令能正常执行,没有启动级报错。比如配置语法错误、Node 版本不兼容、模块找不到。
第二,看扫描范围是否符合预期。你可以故意在一个文件里写一句var x = 1,如果没被报出来,说明文件可能被忽略了,或者规则没有开起来。
第三,看输出是否容易阅读。默认输出会列出文件路径、规则名和错误位置。如果输出混入了大量无关文件,先改忽略配置,而不是加更多规则。
这一步的关键是把工具本身的链路打通。工具跑不起来就谈规则,等于还没学会走路就开始跑。
2.4 编辑器接入能提升动力
命令行 lint 适合 CI 和批量检查,但开发者日常应该尽量在编辑器里就看到问题。Oxlint 生态里通常有对应的编辑器插件。如果你用的编辑器支持 LSP,可以按官方文档接入。
但这里要给一句提醒:编辑器报错和命令行 lint 不能完全画等号。编辑器可能使用缓存,命令行每次都是真实运行。我见过不少案例,开发者在编辑器里看不到任何问题,提交到 CI 却开始报错。遇到这种情况,不要怀疑规则有 bug,先跑一遍命令行,把两边的版本和配置对比一下。
3. 规则怎么配,才算“Opinionated”
3.1 优先级应该再清晰一点:Correctness 首先要管住
anti-slop 不是把风格类规则放在最前面。代码风格当然重要,但风格问题不影响运行,真正影响维护安全的是“正确性”和“可疑模式”。
我建议按下面这个顺序配置:
- 正确性类:宽松相等、空块、恒定条件、未使用变量、重复逻辑。
- 可疑类:可能出现空值但不处理、类型断言过于随意、无限循环风险。
- 性能类:不必要的计算、同步阻塞、重复调用高开销函数。
- 风格类:变量命名、函数长度、注释风格。
正确性类规则优先开成 error。一个项目如果连未使用变量都允许存在,后面再加别的严格规则,团队会觉得 lint 根本没有原则。除了没有原则,还有一个现实问题:未使用变量往往是重构没做干净的信号,留着它就是留着隐患。
3.2 典型 slop 模式,以及 anti-slop 会怎么处理
下面这些模式是 anti-slop 思路里很容易出现的高频对象。这里不写死规则名,因为不同版本的 Oxlint 规则名和默认级别会有差异,但核心判断可以看这张表。
| 典型 SLOP 模式 | 代码里常见的样子 | anti-slop 倾向 |
|---|---|---|
| 宽松相等 | if (res == null) | 强制===和!== |
| 空 catch | catch (e) {} | 至少要注释原因或记录日志 |
| 未使用变量 | 参数、导入、局部变量残留 | 直接报错 |
| 恒真恒假条件 | while (true)且没有退出条件 | 提示条件可能写错 |
| 调试日志 | console.log 随手打,不清理 | 或允许统一日志入口,禁止散装输出 |
| 复读注释 | 注释把代码翻译成中文 | 注释应该解释为什么 |
| 无用抽象 | 一个函数只包一行代码,名字还很抽象 | 提示过度设计 |
| 隐性类型转换 | 字符串直接加减 | 强制显式处理类型 |
这些模式放在一起,你会发现一个共性:它们都不是“语法错误”,而是“可读性债务”。anti-slop 的核心态度就是:债务不该由后来人默默承担。
3.3 关于 AI 生成代码,特殊在哪里
现在很多编辑器都集成了 AI 辅助能力,像 CodeBuddy 这类工具在生成 lint 配置或补全代码时,往往会按“基线可用”的标准输出,距离 anti-slop 这种严格门禁还差得很远。AI 生成代码有一个典型特点:局部正确,整体不自觉。
什么意思?AI 补全一个函数时,它能保证语法正确,也能保证大概思路对,但它不会考虑你项目里已经存在的命名风格、抽象层次、错误处理约定。它很容易生成:
- 没用的 if 判断。
- 重复出现的字段判断。
- 为了泛化而泛化的类型参数。
- 和上下文逻辑重复的注释。
- 复制过来的相似函数,只改动了一半。
这些内容恰好是传统 lint 规则不太关注、anti-slop 风格规则很想收口的部分。所以如果你的团队大量使用 AI 编程辅助,你不能只依赖默认 lint,必须有一套更严格的规则集来约束产线代码。
当然,规则解决不了所有 AI 问题。Code Review 仍然要做。但规则的作用是先把明显不合格的代码挡在外面,让 Review 的精力集中在真正需要判断的地方。
3.4 自定义规则要克制
Oxlint 的扩展性不如 ESLint 生态那么自由,但这不一定是坏事。很多人看到规则不够用,第一反应是立刻写自定义插件。我的建议是:先把现成规则用明白,再评估自定义。大部分项目里,真正该拦的问题已经有现成规则了,只是团队没有开起来。
如果确实需要自定义,注意两点:
- 规则必须服务于具体问题,不要为了“我们团队很严格”而加。
- 规则必须能被明确解释,说不清楚维护价值的规则,很快会被绕过或关掉。
4. 从单文件到全仓,再到 CI 门禁
4.1 先跑单文件,再跑整个仓库
我一直建议按“单文件 -> 单目录 -> 全仓库”的顺序推进。原因很简单:lint 规则具有连锁效应。你开一条规则后,可能一个文件只报 3 处,但这个模式在 200 个文件里都有,全量跑出来的数字会让人瞬间失去信心。
第一次跑全仓之前,先在src下选一个小项目,把它当成试点。跑完先不看报错总数,先看报错集中在哪个规则上。哪些规则是历史遗留,哪些规则是最近新增代码引入的,心里要有数。
如果历史代码大量违规,不要一次性把所有规则都设成 error。把规则设好,但一部分先保持 warn,然后看统计结果。等团队逐步清理完了,再把 warn 升成 error。这个过程不是认输,是渐进式改进。
4.2 批量修复时要控制风险
Oxlint 支持自动修复能力,但自动修复不是万能药。有些规则可以安全修复,比如把var改成let或const;有些规则自动修复可能会改变语义,比如调整表达式结构、改动类型断言。
我的经验是:批量修复前先备份提交一次,然后用--fix之类的参数跑,跑完必须看 diff。哪怕你确定这条规则很安全,也要看。因为当文件数量很多时,diff 里的异常容易被淹没。
需要特别小心的是:
- 生成代码不要批量修,比如接口类型定义、配置常量,改动后可能影响运行。
- 测试代码不要和源码一套标准直接修,测试里的宽松断言有时是为了简化输入。
- 模板文件不要无脑修,字符串模板里的缩进和空格改动可能改变渲染结果。
批量修复不是一次性的,清理完这轮,下一轮应该越来越少。如果每次 merge 都会新增成批旧规则问题,说明 lint 没有真正进入开发流程。
4.3 把 Oxlint 接进 CI,让它成为门禁
本地执行容易偷懒,CI 里跑 lint 才能保证每个人提交代码时都被检查。CI 集成的思路其实不复杂:安装依赖之后执行oxlint,如果规则是 error,命令会以非零状态退出,流水线就会失败。
还可以考虑拆成两步:一步只做 lint,另一步做类型检查或单元测试。lint 跑得快,先失败先反馈,不用等所有检查都跑完才发现问题。
推荐在 push 和 pull request 时都触发。push 触发能尽早发现,pull request 触发能作为合并的前置条件。注意,如果仓库本身就带历史警告,CI 会因为老问题一直失败,团队很容易养成“红着也不管”的习惯。所以接 CI 之前,尽量先清理一轮旧问题,或者把历史问题通过配置豁免,只对新代码保持严格。
4.4 输出格式和验收标准
运行结果怎么算通过?不是“没有红色报错”,而是命令退出码为 0,并且没有被忽略文件的误导。
我建议建立三个明确标准:
- 单流程必须跑通:从安装、读取配置、扫描目录到输出结果,全程无启动异常。
- 严格规则必须拦截:故意写入一条
==,应能稳定报错。 - 全仓数量可控:全量报错数量应该小于一个阈值,并且趋势是下降的。
如果你们团队已经有可观测性体系,还可以把 lint 告警数作为指标上报。每个版本发布时看一眼告警数量,比只看“能不能编译过”更能反映代码健康度。
5. 常见报错和排查链路
5.1 命令没跑通,先看版本和路径
如果执行oxlint直接报command not found,百分之八十是局部安装没生效,或者没有通过 npx 调用。检查:
npx oxlint --version如果 npx 能跑而脚本里不能,多半是 package.json 脚本的 PATH 问题。如果是配置解析失败,错误信息里通常会指出第几行第几个字段,格式问题最好排查。
版本问题也很常见。有些规则在旧版本里不存在,你按文档配置了新规则,旧版本不识别,可能直接报错或被忽略。升级前先看 changelog,至少确认规则名有没有变化。
5.2 规则没生效,先怀疑 ignore 和被覆盖
“我明明配置了,为什么没有任何反应?”这句话我听到过太多次。这时候不要怀疑工具坏了,按下面顺序排查。
第一,检查文件是否真的被扫描。如果文件在dist、build、vendor这类默认忽略目录里,它压根不会触发任何规则。
第二,检查配置是否确实指向了这个文件。如果你在子目录里运行命令,配置的加载规则可能和你以为的不一样。建议始终在项目根目录运行。
第三,检查规则名是否被拼写错误,或者当前版本不支持该名称。
第四,检查同一规则是否在多个层级出现。比如项目根级配置和目录级配置都设置了该规则,后加载的配置可能覆盖了前面的。
第五,检查规则级别确实为error而不是off或warn。warn 在很多时候也会输出,但如果你只看退出码,可能感觉“规则没生效”。
5.3 和 ESLint、Prettier、Biome 一起用,怎么处理冲突
现实中很多仓库不是只用一个工具。ESLint 管复杂规则,Prettier 管格式,Oxlint 管快速门禁,Biome 可能还承担一部分处理。工具多了必然会有重叠。
处理原则是:职责边界要清楚。
- 格式类问题统一交给 Prettier,不要在 Oxlint 里开一堆缩进、引号、分号规则,否则两边标准不一致只会互相打架。
- 正确性和可疑模式类规则,Oxlint 和 ESLint 可以同时开,但同一规则不要两边都设成 error。你可以让 Oxlint 跑第一遍,ESLint 跑第二遍,或者反过来。
- 如果两个工具对同一模式给出相反建议,优先以项目里已经稳定运行的方案为准,不要为了“用上更严格规则”而把现有风格推翻。
还有一个更常见的坑:Oxlint 配置好了,但团队还在旧 ESLint 配置里加了同样的规则,两边都不一致。建议把所有 lint 配置放在同一套文档里统一说明,减少认知负担。
5.4 满屏误报时,先别关规则
误报是最容易导致团队弃用 lint 的原因。但直接关规则往往不是最优解。面对误报,先回答三个问题:
- 是不是输入类型没写清楚,导致规则只能按最保守方式判断?
- 是不是当前上下文有特殊约定,需要选优豁免?
- 是不是规则本身不适合这个项目,有更合适的替代方案?
如果确实要豁免,我建议采用行级或文件级的显式注释,而不是全局关闭。全局关闭等于承认这条规则失效,以后再想开回来阻力极大。行级豁免至少留下一个痕迹,让后来人知道这里是被有意放过的。
6. 什么时候该用,什么时候别硬上
6.1 适合使用 anti-slop 场景
正在建设新前端工程,还没有历史包袱的时候,最适合引入。新项目的规则从第一天就保持严格,后面维护成本会低很多。
TypeScript 项目、多模块项目、多人协作项目也适合。规模越大,代码风格和边界处理越需要被统一。如果是个人项目,自己说了算,规则松一点无所谓;一旦有第二个人参与,规则就成了协作契约。
有大量 AI 生成代码的团队也需要这一套。AI 辅助工具越来越多,代码库里的“能力垃圾”会快速增长。严格 lint 至少能把最有问题的部分拦截住。
6.2 不适合硬上的场景
只有一个例外级别的旧项目,全仓代码已经混乱到无法快速修复,这时候不要一上来就全开 error。规则是对的,但落地节奏要错开。先把新代码和改动文件纳入严格检查,存量代码单独列一个清理任务。
还有一个场景:团队成员没有接受度。如果大家不理解为什么要有观点,只把 lint 当作“领导要的检查”,那么再好的规则也会被绕过。你可以先跑一两次示例,用真实报错说明哪些问题会导致线上故障,而不是堆术语。
6.3 渐进式落地的一个可用节奏
我推荐下面这个节奏:
- 第一周:安装 Oxlint,只开默认配置,跑通命令,输出基线数据。
- 第二周:把 anti-slop 风格倾向明显的规则加到 warn,全仓盘点问题量。
- 第三周:清理新代码和重要模块,把这部分规则改成 error。
- 第四周:接 CI,制定“不得新增违规”门禁。
- 之后:按模块继续清理历史问题,逐步将 warn 升级为 error。
与其一次推到全量,不如让规则在一个稳定范围内先运转起来。lint 是长期机制,不是一次性搬家。
6.4 长期维护时,把自己从“用户”变成“维护者”
最后想说的是,任何规则集都不应该是一成不变的。项目会变化,依赖会升级,团队习惯会调整。真正的 anti-slop 不是某个静态文件,而是一套持续维护的规则策略。
每隔一两个季度,应该重新审视一次配置文件。把已经长期零告警的规则继续保留,把误报频繁且没有实际收益的规则降级,把新出现的问题模式补充进去。规则集如果三年不变,它就不是门禁,而是一块需要拆掉的旧墙。
踩过这些坑之后,我的体会是:很多所谓 lint 问题,都不是工具能力不够,而是前置环境和输入材料没有处理干净。anti-slop 给我的最大启发不是“把规则配满”,而是明确说出哪些东西不可以。当你开始认真定义“不可以”,代码质量才会真的往前走。