DEV Community

Cover image for Post-Mortem: Why ESLint Performance Failed (And the 100x Fix)
Ofri Peretz
Ofri Peretz

Posted on • Edited on • Originally published at ofriperetz.dev

Post-Mortem: Why ESLint Performance Failed (And the 100x Fix)

Developer velocity dies when CI takes 45 seconds to lint. Here is the technical post-mortem of why traditional linting failed, and how we engineered a 100x speedup into the static analysis ecosystem.

Your CI is slow. Your pre-commit hooks timeout. Developers disable linting to ship faster.

The culprit? eslint-plugin-import.

The Performance Gap

┌─────────────────────────────────────────────────────┐
│ Linting 10,000 files                                │
├─────────────────────────────────────────────────────┤
│ eslint-plugin-import:      45.0s  ███████████████████│
│ eslint-plugin-import-next:  0.4s  ▏                  │
└─────────────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

That's 100x faster. Not a typo.

Why Is It So Slow?

1. Cold Module Resolution

// eslint-plugin-import resolves EVERY import from scratch
import { Button } from '@company/ui'; // Resolves entire package

// On every lint run. Every file. Every import.
Enter fullscreen mode Exit fullscreen mode

2. The no-cycle Problem

Click to see why no-cycle is a performance killer

The import/no-cycle rule builds a complete dependency graph.

For N files with M imports each:

  • Time complexity: O(N × M²)
  • Memory: Entire graph in RAM
  • Result: OOM on large monorepos
# Real GitHub issues:
# "import/no-cycle takes 70% of lint time" (#2182)
# "OOM checking circular dependencies"
# "Minutes to lint a monorepo"
Enter fullscreen mode Exit fullscreen mode

3. No Caching

Every lint run repeats the same work. No incremental analysis.

The Solution

We rebuilt module resolution with:

Feature eslint-plugin-import eslint-plugin-import-next
Caching ❌ None ✅ Cross-file shared cache
Cycle Detection O(N × M²) O(N) with memoization
TypeScript 🐌 Slow resolver ⚡ Native TS support
Flat Config ⚠️ Partial ✅ Native

Quick Migration

npm uninstall eslint-plugin-import
npm install --save-dev eslint-plugin-import-next
Enter fullscreen mode Exit fullscreen mode
// eslint.config.js
import importNext from 'eslint-plugin-import-next';

export default [importNext.configs.recommended];
Enter fullscreen mode Exit fullscreen mode

That's it. Same rules, 100x faster.

Benchmark It Yourself

⭐ Star on GitHub and Run the Benchmark

# Compare on your own codebase
time npx eslint --no-cache . # With eslint-plugin-import
time npx eslint --no-cache . # With eslint-plugin-import-next
Enter fullscreen mode Exit fullscreen mode

📦 npm: eslint-plugin-import-next
📖 Migration Guide


The Interlace ESLint Ecosystem
Interlace is a high-fidelity suite of static code analyzers designed to automate security, performance, and reliability for the modern Node.js stack. With over 330 rules across 18 specialized plugins, it provides 100% coverage for OWASP Top 10, LLM Security, and Database Hardening.

Explore the full Documentation

© 2026 Ofri Peretz. All rights reserved.


Build Securely.
I'm Ofri Peretz, a Security Engineering Leader and the architect of the Interlace Ecosystem. I build static analysis standards that automate security and performance for Node.js fleets at scale.

ofriperetz.dev | LinkedIn | GitHub

Top comments (0)