Engineering the 100x Speedup: A Static Analysis Performance Report

A data-driven report on optimizing static analysis at scale. How we engineered a 100x speedup in the industry's most used linter plugin.

4 min read
Engineering the 100x Speedup: A Static Analysis Performance Report

Up to 100x faster on circular dependency detection. Reproducible benchmarks. Full methodology.

๐Ÿ”„ Drop-in replacement โ€” 100% compatible with all eslint-plugin-import rules, but faster, LLM-optimized error messages, and fewer false positives/negatives.

TL;DR

Benchmark1K Files5K Files10K Files
Core Rules (9)1.6x3.3x5.2x
Recommended Preset1.4x3.0x5.5x
no-cycle Only25.7x54.9x~100x*

*10K projection based on O(nยฒ) scaling pattern observed at 1Kโ†’5K. We stopped at 5K because eslint-plugin-import would take 10+ minutes.


Why eslint-plugin-import is Slow

The original eslint-plugin-import uses an O(nยฒ) module resolution algorithm:

  1. For each file, parse all imports
  2. For each import, resolve the full module path
  3. For no-cycle, traverse the entire dependency graph for every file

This creates quadratic complexity. On 5,000 files with interconnected imports, the no-cycle rule alone takes 148 seconds.

How eslint-plugin-import-next Fixes This

We rewrote the core algorithms:

  1. Cached module resolution โ€” resolve each path once, cache permanently
  2. Incremental graph building โ€” build the dependency graph incrementally, not per-file
  3. Cycle detection with Tarjan's algorithm โ€” O(n) instead of O(nยฒ)

Result: 2.71 seconds for the same 5,000 files.


Benchmark 1: Core Rules (9 rules)

Both plugins configured with identical rules:

Fileseslint-plugin-importeslint-plugin-import-nextSpeedup
1,0002.80s1.78s1.6x
5,00019.04s5.76s3.3x
10,00058.67s11.26s5.2x

Takeaway: Even with basic rules, the performance gap grows with codebase size.


Using the full recommended configuration from each plugin.

Fileseslint-plugin-importeslint-plugin-import-nextSpeedup
1,0002.42s1.78s1.4x
5,00018.43s6.07s3.0x
10,00057.74s10.57s5.5x

Takeaway: Recommended presets show similar scaling โ€” 5.5x faster at 10K files.


Benchmark 3: no-cycle Rule Only

This is where the difference is massive. The no-cycle rule detects circular dependencies.

Fileseslint-plugin-importeslint-plugin-import-nextSpeedup
1,00027.03s1.05s25.7x
5,000148.59s2.71s54.9x
10,000~600s (projected)*~5s (projected)~100x

*10K Projection Note: Based on the O(nยฒ) scaling observed from 1Kโ†’5K (27sโ†’148s = 5.5x increase for 5x files), we project eslint-plugin-import would take ~10 minutes at 10K files. We didn't run this because waiting 10+ minutes per iteration is impractical. eslint-plugin-import-next scales linearly (O(n)), so ~5s is expected.

Takeaway: If you use no-cycle (and you should), the speedup is 25-100x depending on codebase size.

text
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ no-cycle Rule: 5,000 files                                     โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ eslint-plugin-import:      148.59s โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ”‚
โ”‚ eslint-plugin-import-next:   2.71s โ–ˆ                           โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Why no-cycle is Critical

Circular dependencies cause:

  • Build failures with tree-shaking
  • Runtime bugs with undefined imports
  • Memory leaks in bundlers
  • Test flakiness from initialization order

Most teams disable no-cycle because it's too slow. With eslint-plugin-import-next, you can finally enable it.


Methodology

Apple-to-apple comparison โ€” full source code

SpecDetails
Codebase sizes1,000 / 5,000 / 10,000 JavaScript files
Iterations3-5 runs per size, per plugin
FixturesRealistic JS files with named/default imports, barrel files, cross-file dependencies
EnvironmentNode v20.19.5, Apple Silicon M1 (arm64), ESLint v9.17.0
CacheCleared between each run

Run It Yourself

bash
git clone https://github.com/ofri-peretz/eslint-benchmark-suite.git
cd eslint-benchmark-suite
npm install
npm run generate:import
npm run benchmark:import
npm run benchmark:import-recommended
npm run benchmark:import-no-cycle

Migration Takes 2 Minutes

bash
# Remove old plugin
npm uninstall eslint-plugin-import

# Install new plugin
npm install --save-dev eslint-plugin-import-next
javascript
// eslint.config.js
import importNext from 'eslint-plugin-import-next';
export default [importNext.configs.recommended];

Resources

๐Ÿ“ฆ npm: eslint-plugin-import-next ๐Ÿ“Š Benchmark Suite ๐Ÿ“– Full Rule List

โญ Star on GitHub


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