Understanding ESLint Architecture and Execution

How ESLint Works

ESLint parses code into an Abstract Syntax Tree (AST) using Espree (based on Acorn), then applies configured rules (core or plugin-based) to traverse the AST and flag or fix violations.

  • Config can extend shareable configs or be fully custom
  • Execution happens via CLI, CLIEngine (Node.js), or IDE extensions
  • Rules can be synchronous or asynchronous (especially with TypeScript)

Common Enterprise-Scale ESLint Problems

1. Slow Linting in Large Repos

Monorepos with deeply nested packages or thousands of files often experience 10–30s linting durations due to:

  • Excessive glob matching
  • Redundant config resolution across packages
  • Heavy plugin rule processing (e.g., @typescript-eslint)
eslint --ext .js,.ts src/  # can take 20s+ in large codebases

Optimize using caching, narrowing globs, and filtering affected files in CI.

2. Conflicting Rule Definitions

Multiple rule sets (e.g., Airbnb + Prettier + custom rules) may conflict, producing contradictory outputs or autofix loops.

{
  "extends": ["airbnb-base", "prettier"]
}

Always ensure rule priorities are clear. Use eslint-config-prettier last to disable formatting conflicts.

3. IDE vs CLI Discrepancies

VS Code extensions may use a global ESLint version, whereas CLI uses the local one—causing different linting behavior.

// VSCode settings.json
"eslint.nodePath": "/usr/local/lib/node_modules"

Force IDE to use workspace ESLint: "eslint.workingDirectories": [{ "mode": "auto" }].

4. ESLint Not Picking Up tsconfig.json

TypeScript-based rules (e.g., no-unused-vars) fail without a proper parser config.

{
  "parserOptions": {
    "project": "./tsconfig.json"
  }
}

Ensure tsconfig path is correct and matches the file's location. Missing project path causes parsing failures or skipped rules.

5. CI Failures Due to Lint Errors in Auto-Generated Code

Generated files (e.g., GraphQL types, OpenAPI clients) can cause lint rule violations. ESLint doesn't exclude them by default.

"eslintIgnore": ["**/__generated__/**"]

Alternatively, configure .eslintignore or conditional rules based on globs.

Advanced Diagnostics and Instrumentation

1. Measuring Linting Time per File

Use the --timing flag to identify slow files/rules (ESLint v8+):

eslint . --timing --format json

2. Rule Performance Profiling

Use the community tool eslint-plugin-perf to flag inefficient rules in your config.

3. Debugging Config Resolution

Use eslint --print-config <file> to check resolved rules and plugins for any file.

eslint --print-config src/index.ts

Fixes and Long-Term Remediation Strategies

1. Speed Optimization

  • Use --cache with a proper CI cache strategy
  • Split linting into parallel jobs across packages/modules
  • Lint only changed files in PRs (with tools like lint-staged)

2. Config Hygiene

  • Centralize base config via internal package or monorepo root
  • Use overrides for package-specific rules
  • Regularly audit dependencies for deprecated plugins or rules

3. Prettier Integration Best Practices

  • Place eslint-config-prettier and eslint-plugin-prettier last in the extends chain
  • Run prettier --check separately if using it as a formatter, not via ESLint

4. TypeScript Compatibility

  • Use @typescript-eslint/parser and plugin
  • Always point parserOptions.project to tsconfig.json
  • Avoid linting output directories (e.g., dist, build)

Conclusion

ESLint is indispensable for maintaining high code quality in JavaScript and TypeScript projects. However, mismanagement of configurations, rule conflicts, and scaling issues can reduce its effectiveness and frustrate developers. By implementing efficient linting strategies, maintaining clean config hierarchies, and leveraging ESLint's advanced diagnostics, teams can achieve consistent, performant, and CI-compliant linting across even the largest repositories.

FAQs

1. Why is ESLint so slow on my monorepo?

Large file counts, multiple plugins, and recursive config loading all contribute. Use caching, narrower globs, and per-package linting for speed.

2. How do I make ESLint respect my tsconfig.json?

Ensure you use @typescript-eslint/parser and specify parserOptions.project. Place tsconfig in the same directory or update the path accordingly.

3. Why are Prettier and ESLint giving different results?

Prettier formats code, ESLint enforces rules. To avoid conflicts, use eslint-config-prettier to disable overlapping ESLint rules.

4. Can I auto-fix ESLint errors safely in CI?

Yes, but auto-fix should be restricted to non-breaking rules. Always audit diffs in PRs or restrict auto-fix to local pre-commit hooks.

5. How do I apply different ESLint rules to different file types?

Use the overrides field in .eslintrc to specify file glob patterns and corresponding rules or parser options.