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

A technical analysis of performance degradation in large-scale static analysis. The engineering journey from 45s to 0.4s linting times.

2 min read
Post-Mortem: Why ESLint Performance Failed (And the 100x Fix)
Share:

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

text
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Linting 10,000 files                                โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ eslint-plugin-import:      45.0s  โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ”‚
โ”‚ eslint-plugin-import-next:  0.4s  โ–                  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

That's 100x faster. Not a typo.

Why Is It So Slow?

1. Cold Module Resolution

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

// On every lint run. Every file. Every import.

2. The no-cycle Problem

{% details 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
bash
# Real GitHub issues:
# "import/no-cycle takes 70% of lint time" (#2182)
# "OOM checking circular dependencies"
# "Minutes to lint a monorepo"

{% enddetails %}

3. No Caching

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

The Solution

We rebuilt module resolution with:

Featureeslint-plugin-importeslint-plugin-import-next
CachingโŒ Noneโœ… Cross-file shared cache
Cycle DetectionO(N ร— Mยฒ)O(N) with memoization
TypeScript๐ŸŒ Slow resolverโšก Native TS support
Flat Configโš ๏ธ Partialโœ… Native

Quick Migration

bash
npm uninstall eslint-plugin-import
npm install --save-dev eslint-plugin-import-next
javascript
// eslint.config.js
import importNext from 'eslint-plugin-import-next';

export default [importNext.configs.recommended];

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

Benchmark It Yourself

bash
# Compare on your own codebase
time npx eslint --no-cache . # With eslint-plugin-import
time npx eslint --no-cache . # With eslint-plugin-import-next

๐Ÿ“ฆ 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

Built with Nuxt UI โ€ข ยฉ 2026 Ofri Peretz