Skip to content

Tooling Engineering — June 2026

ESLint + Prettier in 2026 — Velocity X's Tight Biome Setup That Catches AI Bugs

All articles
🔍 ⚡ 🤖

One Binary. Zero Config. Catches Unused Awaits Before They Ship.

If you've been living under a rock, ESLint + Prettier stopped being the obvious default around mid-2025. The ecosystem reached consensus: ESLint is bloated, Prettier conflicts with it on every update, and the separate-tool-chain approach wastes 500ms+ per lint cycle. Biome happened. One binary, written in Rust, compiled to run everywhere. Instant. Velocity X ditched ESLint + Prettier in favour of Biome months ago. Not for the speed (though that's nice). The real win is the *tight ruleset* that catches patterns AI models love to generate: unused await expressions, dangling promises, mis-typed React props, implicit any, dead branches. These are the bugs that slip past human code review because the syntax is legal. Here's how it works and why it matters. Why ESLint + Prettier became a liability ESLint started as a linter. Prettier started as a code formatter. Someone then decided Prettier should work inside ESLint, so now you have two tools arguing about indentation, semicolons, and quote style on every save. You need eslint-plugin-prettier to bridge them. You need eslint-config-prettier to stop them from fighting. You need 15+ npm packages and a 40-line config file. Worse: the Node-based startup cost is brutal. ESLint takes ~300-400ms to bootstrap, even if your actual linting is instant. On a machine with multiple projects, or during rapid iteration, that overhead compounds. Run eslint 50 times a day, lose 4+ minutes. More importantly: ESLint is *permissive*. Its rules are suggestions. You enable what matters to you, disable what doesn't. That flexibility is a liability when you're shipping code with an AI pair-programmer. Claude, GPT-4, whatever—they generate syntactically correct code that's semantically wrong: await expressions that do nothing, promises that never resolve, type assertions that hide bugs. ESLint allows these patterns by default. Biome: definition, not config Behold: Biome is a linter + formatter unified into a single, opinionated Go binary. Written in Rust, distributed as a precompiled binary for every OS. No npm install of 47 packages. No 40-line config. Instead, a 10-line biomejs.json that defines—not configures—your code quality rules. Opinionated means Biome ships with sensible defaults. It *enforces* no unused variables. It *requires* explicit void returns on promises. It catches mis-typed React props and circular dependencies. These rules are on by default because the maintainers decided they're correct, not optional. You can tune a few rules if you have a good reason. You're not supposed to disable half the ruleset to match your team's bad habits. Velocity X's Biome config runs in under 100ms, catches AI-generated bugs before they hit pre-commit, and has prevented more shipping disasters than we care to admit. Velocity X's Biome ruleset (the money pattern) Here's the biomejs.json for Velocity X: ```json { "organizeImports": { "enabled": true }, "linter": { "enabled": true, "rules": { "recommended": true, "suspicious": { "noExplicitAny": "error", "noImplicitAnyLet": "error", "useValidTypeof": "error" }, "correctness": { "noUnusedVariables": "error", "noUnusedImports": "error", "noUnreachable": "error", "noConstAssign": "error", "useIsNan": "error" }, "performance": { "noBarrelExport": "warn" }, "style": { "noNonNullAssertion": "warn", "useConst": "error", "useBlockStatements": "warn" } } }, "formatter": { "enabled": true, "indentSize": 2, "lineWidth": 100, "trailingComma": "es5" }, "javascript": { "formatter": { "trailingCommas": "es5", "semicolons": "always", "arrowParentheses": "asNeeded" } }, "json": { "parser": { "allowComments": false, "allowTrailingCommas": false }, "formatter": { "enabled": true } } } ``` The key rules: - **noExplicitAny + noImplicitAnyLet**: TypeScript with any is TypeScript without types. Fail hard on implicit any. This catches "just make it any" shortcuts that Claude tries. - **noUnusedVariables + noUnusedImports**: Dead code is a liability. Remove it. - **noBarrelExport (warn)**: Barrel exports (/index.ts re-exporting everything) create circular-dependency black holes. Warn, don't error, because some patterns need them. - **useIsNan**: NaN is not equal to itself (NaN !== NaN). Biome requires Number.isNaN(). Catches a common AI mistake. - **noNonNullAssertion (warn)**: The exclamation mark (!x) tells TypeScript "trust me". Biome warns because trust usually means you didn't think it through. - **useConst**: let is fine for loops. For everything else, const prevents accidental reassignment. Biome enforces it. The formatter is vanilla: 2 spaces, 100 char line width, trailing commas ES5-style (because Node.js). Nothing exotic. AI-bug-catching rules in detail Here's where Biome shines. These are the patterns that ship and break in production: **Unused await:** ```typescript // ❌ AI generates this, Biome catches it async function fetchUser(id: string) { await fetch(`/api/users/${id}`); return { id, name: 'Unknown' }; // forgot to capture response } // ✅ Biome rule: suspicious/useAwait (implied by strict mode) ``` **Dangling promise:** ```typescript // ❌ AI thinks this is fine function saveFile(path: string) { writeFile(path, data); // missing await, returns unfulfilled promise } // ✅ Biome catches it in strict mode (requires void or await on promises) ``` **Implicit any:** ```typescript // ❌ Silently dangerous const config = JSON.parse(raw); // type is any config.databaseUrl.split('.')[0]; // crashes if databaseUrl missing // ✅ Biome: noImplicitAnyLet errors immediately. You must assert the type. ``` **Circular dependency:** ```typescript // file-a.ts imports from file-b.ts // file-b.ts imports from file-a.ts // Biome's ESM analyzer catches this before runtime import { a } from './a'; // Biome: circular ``` Every one of these has shipped in a PR because the human reviewer missed it. Biome catches them pre-commit. Editor integration + pre-commit Install Biome in your project: ```bash npm install -D @biomejs/biome ``` Add to package.json: ```json { "scripts": { "lint": "biome lint --write src/", "check": "biome check --write src/", "format": "biome format --write src/" } } ``` Your editor (VS Code, Cursor, WebStorm) needs the Biome extension. Install it, enable Format on Save, and every file formats + lints on blur. No extra keypresses. For pre-commit, integrate with Lefthook: ```yaml pre-commit: parallel: true commands: biome: glob: "src/**/*.{ts,tsx,js,jsx,json}" run: npx biome check --write {staged_files} ``` Biome's --write flag auto-fixes formatting and import order. If there's an error (unused variable, implicit any), the hook fails and you fix it. Total time: 50-100ms. Six FAQs **Q: Will this break my existing ESLint config?** Partially. Biome has different rule names and defaults. You'll need to adjust your rules.json once. After that, it's set-and-forget. **Q: Does Biome support TypeScript?** Yes. Full TypeScript support, including resolving type errors in files. No separate tsconfig needed for linting. **Q: Can I use ESLint plugins with Biome?** No. Biome is standalone. If you rely on esoteric plugins (e.g., for a framework Biome doesn't know), you'll need to keep ESLint. Most teams don't. **Q: What about Prettier plugins (markdown, YAML)?** Biome focuses on JavaScript/TypeScript. For markdown or YAML, keep Prettier or use a separate tool. Biome doesn't pretend to handle everything. **Q: Will upgrading Biome break my rules?** Unlikely. Biome's versioning is stable. Rule changes are documented. The JSON config format is backward-compatible within major versions. **Q: How do I migrate from ESLint + Prettier?** Delete .eslintrc.js, .prettierrc, and the eslint/prettier packages. Install Biome. Create biomejs.json with your rules. Run biome check --write. Done. Takes 10 minutes for most projects. Bottom line ESLint + Prettier were the right choice in 2020. In 2026, they're technical debt. Biome is faster, catches more bugs (especially AI-generated ones), and requires a tenth of the configuration. Velocity X standardises on it because the setup cost is negligible and the upside is real: developers ship better code, pre-commit is instant, and the rulesets enforce standards instead of suggesting them. If you're still maintaining a 40-line ESLint config, upgrade. You'll wonder why you waited.
Let us make some quick suggestions?
Please provide your full name.
Please provide your phone number.
Please provide a valid phone number.
Please provide your email address.
Please provide a valid email address.
Please provide your brand name or website.
Please provide your brand name or website.