The ESLint 9 Flat Config Disaster: Why Engineering Teams Are Migrating to Biome in 2026
The Breaking Point of JavaScript Linting
For over ten years, setting up static analysis in a JavaScript or TypeScript repository followed an unquestioned ritual: initialize .eslintrc.json, extend eslint:recommended and @typescript-eslint/recommended, configure .prettierrc for formatting, and install eslint-config-prettier to prevent the two tools from fighting over trailing commas.
It was never fast, and it was rarely pleasant. But it was familiar, stable, and deeply ingrained into the collective muscle memory of millions of developers.
Then came ESLint 9.
In an ambitious effort to modernize its internal architecture, the ESLint team permanently deprecated the legacy .eslintrc format and mandated Flat Config (eslint.config.js or eslint.config.mjs). What was designed to bring clarity and programmability to configuration files quickly turned into an ecosystem disaster.
Thousands of community plugins broke overnight. Plugin maintainers abandoned their repositories rather than rewrite their rules for the new API. Developers upgrading to ESLint 9 were greeted with unhelpful circular import crashes, missing type definitions, and sprawling 150-line array-merging scripts just to achieve basic TypeScript linting.
In 2026, developer patience has officially run out. Instead of rewriting their configurations for Flat Config, engineering teams across startups and enterprises are executing a clean break: deleting ESLint and Prettier entirely in favor of Biome.
1. The Flat Config Calamity: Breaking Ten Years of Ecosystem Trust
To understand why the migration to Biome has been so explosive, look at what configuring ESLint 9 actually requires in a modern full-stack project.
In the legacy .eslintrc.json, extending standard rules was straightforward:
// Legacy .eslintrc.json (Deprecated in ESLint 9)
{
\"extends\": [
\"eslint:recommended\",
\"plugin:@typescript-eslint/recommended\",
\"prettier\"
]
}
In ESLint 9, that simple declaration mutated into an imperative JavaScript array where developers must manually import plugins, configure language options, merge shared rule objects, and resolve compatibility shims:
// Modern ESLint 9 Flat Config (eslint.config.mjs)
import js from \"@eslint/js\";
import tsPlugin from \"@typescript-eslint/eslint-plugin\";
import tsParser from \"@typescript-eslint/parser\";
import prettierConfig from \"eslint-config-prettier\";
import { FlatCompat } from \"@eslint/eslintrc\";
const compat = new FlatCompat();
export default [
js.configs.recommended,
{
files: [\"**/*.ts\", \"**/*.tsx\"],
languageOptions: {
parser: tsParser,
parserOptions: { project: \"./tsconfig.json\" },
},
plugins: { \"@typescript-eslint\": tsPlugin },
rules: { ...tsPlugin.configs.recommended.rules },
},
...compat.extends(\"plugin:legacy-plugin/recommended\"),
prettierConfig,
];
If one plugin in your dependency tree fails to support Flat Config natively, your entire CI build fails with obscure runtime errors. Developers were forced to spend entire engineering sprints debugging their linter instead of shipping business features.
2. Enter Biome: The Rust-Powered Unified Toolchain
While the ESLint ecosystem fractured, Biome (the open-source successor to Rome, governed under the Biome maintainer group) quietly reached full enterprise maturity.
Engineered in Rust, Biome eliminates the historical divide between linting and formatting. It replaces both ESLint and Prettier with a single, unified binary governed by a simple, declarative biome.json file:
// Modern, Zero-Config Biome (biome.json)
{
\"$schema\": \"https://www.google.com/url?q=https://biomejs.dev/schemas/1.8.3/schema.json%5C&source=gmail&ust=1792198419803000&sa=E",
\"organizeImports\": { \"enabled\": true },
\"linter\": {
\"enabled\": true,
\"rules\": { \"recommended\": true }
},
\"formatter\": {
\"enabled\": true,
\"indentStyle\": \"space\",
\"indentWidth\": 2
}
}
There are no parser plugins to install. There are no compatibility wrappers. TypeScript, JSX, and modern ECMAScript features are parsed natively out of the box with zero configuration.
3. The 35x Performance Chasm in CI/CD
Beyond configuration simplicity, the decisive factor driving enterprise adoption is raw execution speed.
In legacy Node.js, running ESLint and Prettier across a 200,000-line monorepo requires launching two separate V8 runtimes, repeatedly parsing source code into JavaScript ASTs, and executing hundreds of interpreted rule passes. In CI pipelines, running npm run lint routinely consumed 45 to 90 seconds per pull request.
Biome runs on a high-performance multi-threaded Rust architecture:
- Unified AST Traversal: Formatting, import sorting, and lint rule analysis execute in a single parallel pass over the same in-memory AST.
- Sub-Second Execution: That same 200,000-line repository that took 60 seconds under ESLint is formatted and linted by Biome in under 350 milliseconds.
- Instant Hot-Reload in Editors: In VS Code, Neovim, and WebStorm, Biome's language server protocol (LSP) formats and checks code on every keystroke with zero noticeable CPU spike or fan noise.
For organizations running hundreds of daily pull requests, slashing lint steps from two minutes to three seconds saves thousands of developer hours and eliminates CI runner queues.
4. Replacing Both ESLint and Prettier with a Single Binary
The greatest architectural victory of Biome is eliminating the conflict between linters and formatters.
For a decade, developers lived with a fragile compromise: ESLint analyzed code quality, while Prettier formatted syntax. But because ESLint also included stylistic rules (semicolons, quotes, indentation), the two tools frequently collided, requiring special disable-rules and configuration shims.
Biome treats formatting and linting as symbiotic tasks. When Biome applies an automated fix (such as removing an unused import or collapsing an unnecessary ternary), it formats the resulting code simultaneously according to your style rules. You never have to run eslint --fix followed by prettier --write again.
5. What the Linter Wars Teach Us About Tooling Fatigue
The decline of ESLint carries a profound lesson for developer platform maintainers: breaking changes must provide 10x value, not 10x friction.
When ESLint introduced Flat Config, it asked millions of developers to endure massive migration pain without offering any noticeable performance or feature improvements in return. The linter was just as slow, but twice as hard to configure.
Biome seized the moment by offering the inverse: zero configuration and a 35x speedup. When developers are offered an effortless path to delete hundreds of lines of configuration while making their CI builds run thirty times faster, they will vote with their package.json.
Conclusion: The Era of JavaScript-Powered Tooling Is Over
The migration from ESLint to Biome is part of a larger, irreversible trend in software engineering: the complete takeover of developer tooling by native systems languages.
First bundlers moved to Rust and Go (esbuild, Turbopack, Rolldown). Then runtimes modernized (Deno 2, Bun). Now, static analysis and formatting have crossed the chasm.
If your team is currently battling broken Flat Config imports in ESLint 9, stop fighting your tools. Run npx @biomejs/biome init, delete your .eslintrc and .prettierrc, and experience what modern static analysis should feel like.
Build something exceptional.
Custom web design and development, no templates.
