What Is Linting in Programming and Why Use It?

Summarize this article with:
Every codebase has bugs hiding in plain sight. Unused variables, inconsistent formatting, a missing break statement in a switch case. A linter catches these problems before your code ever runs, and understanding what is linting in programming helps you see why most professional teams treat it as non-negotiable.
This article covers how linters work, what they catch that compilers and tests miss, and which tools dominate each language. You’ll also learn how to set up linting rules, run linters in CI/CD pipelines, and avoid the common mistakes that make teams abandon the practice entirely.
What Is Linting in Programming

Linting is the process of running a specialized tool, called a linter, against source code to detect errors, bugs, style violations, and suspicious patterns before the code ever runs.
It’s a form of static code analysis. The linter reads your code, checks it against a set of rules, and reports problems. No execution required.
The term dates back to 1978. Stephen C. Johnson, a computer scientist at Bell Labs, created the original “lint” utility for the C programming language while debugging a Yacc grammar and dealing with portability issues from porting Unix to a 32-bit machine.
The name was borrowed from literal lint (the fluff that collects in a clothes dryer). Johnson’s tool acted like a lint trap, catching small, unwanted fragments in code while leaving the rest intact.
That first lint shipped publicly with Version 7 Unix in 1979. Since then, the concept has spread to nearly every programming language in use today. ESLint handles JavaScript. Pylint and Ruff cover Python. RuboCop works for Ruby. Clippy targets Rust. Stylelint checks CSS. The list keeps growing.
What makes linting different from compiling or testing? A compiler checks if your code can run. Tests check if it does what you expect. A linter checks if it looks right, catches likely mistakes, and enforces your team’s agreed-upon coding standards.
According to ESLint’s own site, the tool sees over 110 million weekly npm downloads and is used by companies like Microsoft, Airbnb, Netflix, and Facebook. ESLint’s 2025 year-in-review reported a 65% increase in weekly downloads over that year alone, climbing from about 42 million to over 70 million.
That kind of adoption tells you something. Linting isn’t optional anymore in most software development workflows. It’s become one of those baseline practices that teams adopt early and rarely abandon.
How a Linter Works

A linter reads your source code, converts it into a structured format, checks that structure against a ruleset, and reports violations. The whole thing happens without executing a single line.
Parsing Source Code into an Abstract Syntax Tree
The first step is parsing. The linter reads raw text and transforms it into an abstract syntax tree (AST), a tree-shaped representation of your code’s structure.
Each node in the AST represents a construct: a variable declaration, a function call, a loop. This tree is what the linter actually analyzes, not the text you see in your editor.
Ruff, the Rust-based Python linter, redesigned its parser in 2024 with a hand-written recursive descent approach. The result was a 2x faster parser, translating to a 20-40% speedup for all linting operations.
Matching Patterns Against Rules
Once the AST exists, the linter walks through it node by node, checking each one against its active rules.
Rule examples:
- “no-unused-vars” flags variables you declared but never used
- “no-implicit-globals” catches accidental global variable creation
- “prefer-const” suggests const over let when a variable is never reassigned
Each rule is basically a function that receives a node and decides if something looks wrong. When it does, the linter records the file path, line number, severity level, and a description of the issue.
Auto-Fixing and Output
Some linters go beyond reporting. They fix issues automatically.
ESLint’s --fix flag can add missing semicolons, reorder imports, and remove unused variables. Ruff’s autofix model handles over 800 built-in rules, and according to Astral (Ruff’s creator), it can replace Flake8, Black, isort, and several other tools in a single pass.
Linters run in multiple places: directly in your terminal, inside code editors like VS Code or JetBrains IDEs through plugins, or as part of build pipelines in CI/CD systems. The output format varies, but the core loop stays the same: parse, check, report.
What Linting Catches That Compilers and Tests Miss
Compilers care about one thing: can this code run? If the syntax is valid and the types check out (in typed languages), the compiler is happy. Tests care about a different thing: does the code produce the expected output?
Linters fill the gap between those two. They catch code that’s technically valid but probably wrong.
Code That Runs Fine but Smells Bad
Unused variables. Your compiler won’t complain if you declare a variable and never reference it. Your linter will. That unused variable might be a sign you forgot to finish implementing something, or it’s leftover from a refactor.
Unreachable code. A return statement buried in the middle of a function means everything below it never executes. Compilers for some languages flag this, but many don’t. Linters reliably do.
According to Wikipedia’s article on lint, modern linters detect issues including “uses of undeclared variables, calls to deprecated functions, spacing and formatting conventions, misuse of scope, implicit fallthrough in switch statements, missing license headers” and more.
Dynamically Typed Language Problems
Linters become especially valuable in languages like JavaScript and Python where the compiler (or interpreter) doesn’t enforce strict rules at build time.
Think about implicit type coercions in JavaScript. Or accidentally using eval() with user input. A linter catches these patterns before they become security problems or runtime crashes.
The IBM Systems Sciences Institute has repeatedly found that fixing a bug after release costs up to 100x more than catching it during the coding phase. Linting is one of the cheapest ways to shift that detection left.
CISQ’s 2022 report estimated that poor software quality costs US companies $2.41 trillion annually. A big chunk of that comes from defects that tools like linters are specifically designed to catch early, before they compound into expensive production issues.
Style and Consistency Issues
This is the less dramatic but equally valuable side of linting.
Inconsistent indentation, mixed naming conventions, tabs vs. spaces, trailing commas. None of these break your program. All of them slow down code review and make your codebase harder to read.
Linters enforce a shared style across an entire team without requiring anyone to memorize a style guide. The rules do the remembering for you.
Common Linters by Programming Language

Every major language has at least one widely adopted linter. Some have several. The right choice depends on your language, your team’s preferences, and how deep you want the analysis to go.
| Language | Popular Linters | Key Strengths |
|---|---|---|
| JavaScript/TypeScript | ESLint | 110M+ weekly npm downloads, plugin ecosystem, auto-fix |
| Python | Pylint, Flake8, Ruff | Ruff is 10–100× faster, 800+ rules, replaces multiple tools |
| Go | golangci-lint, go vet | Built-in tooling, fast CI integration |
| Ruby | RuboCop | Style + lint combined, Rails-aware |
| Rust | Clippy | Official Rust tool, catches idiomatic issues |
| CSS | Stylelint | Works with SCSS, Less, and CSS-in-JS |
JavaScript and TypeScript
ESLint dominates. It’s the default linter for JavaScript and TypeScript projects. Companies like Microsoft, Netflix, and Airbnb run it across their codebases.
ESLint’s 2025 year-in-review showed the tool expanded beyond JavaScript. It now officially supports CSS linting through @eslint/css and HTML linting through html-eslint, making it a multi-language tool for front-end development.
TSLint used to handle TypeScript specifically, but it was deprecated years ago. The TypeScript community moved to ESLint with the @typescript-eslint plugin.
Python
Ruff is the story here. Written in Rust, it runs 10 to 100 times faster than Flake8 or Black. On larger codebases, the difference is even more dramatic.
One developer from Dagster described Ruff as “nearly 1000x faster” on their 250,000-line module, going from 2.5 minutes with Pylint down to 0.4 seconds. Bryan Van de Ven, co-creator of Bokeh, reported Ruff scanned an entire repo in 0.2 seconds compared to 20 seconds with Flake8.
Pylint and Flake8 still see wide use, especially in legacy projects. But Ruff has been adopted by projects like FastAPI, pandas, Apache Airflow, and even Pylint itself (as a pre-commit hook).
Go, Ruby, Rust, and CSS
Go: The language ships with go vet built in. For broader checks, golangci-lint bundles dozens of linters into a single runner. Go’s culture leans heavily toward built-in tooling, so linting adoption is high by default.
Ruby: RuboCop handles both style enforcement and code analysis. It’s Rails-aware, which makes it the standard choice for most Ruby web projects.
Rust: Clippy is the official linter, maintained by the Rust team. It goes beyond basic checks to catch non-idiomatic code patterns. The Rust compiler already does heavy lifting on safety, so Clippy focuses on readability and best practices.
CSS: Stylelint works with plain CSS, SCSS, Less, and CSS-in-JS solutions. It catches things like duplicate properties, invalid hex colors, and inconsistent units.
Linting Rules, Configs, and Presets
A linter without configured rules is just a parser that runs and exits. The rules are what make it useful, and every team eventually ends up customizing them.
Rule Severity Levels
Off: Rule is completely disabled. The linter ignores it.
Warning: The linter reports the issue but doesn’t fail the build. Good for new rules you’re evaluating or patterns you want to flag without blocking work.
Error: The linter reports the issue and returns a non-zero exit code. This fails CI builds and blocks merges. Reserved for rules your team considers non-negotiable.
Getting the severity right matters. Set too many rules to “error” on day one and your team will disable the linter entirely. Too many on “warning” and people stop reading the output.
Configuration Files and Shared Presets
Linter rules live in configuration files. ESLint uses eslint.config.js (or the older .eslintrc format). Pylint uses .pylintrc or pyproject.toml. Ruff uses pyproject.toml or ruff.toml.
Most teams don’t write every rule from scratch. They start with a shared preset.
- Airbnb JavaScript Style Guide is one of the most downloaded ESLint configs
- StandardJS takes a zero-config approach with no semicolons
- Google’s style guides cover Python, Java, Go, and several other languages
You pick a preset, override the handful of rules your team disagrees with, and you’re done. This is how most projects handle linting setup. Took me a while to realize that arguing about individual rules is a waste of time when you can just fork a popular config and move on.
Writing Custom Linting Rules
Sometimes shared presets aren’t enough. Your project might have internal API patterns you want to enforce, or deprecated methods that keep getting used by new team members.
ESLint lets you write custom rules as JavaScript functions that receive AST nodes. You define a visitor pattern: “when the linter visits a CallExpression node, check if it matches this pattern, and report if it does.”
Custom rules are powerful but come with maintenance costs. Every time your internal API changes, the rule might need updating too. Teams that adopt software development best practices around documentation tend to keep their custom rules sustainable. Otherwise, they rot.
Linting in Code Editors and IDEs

Running a linter from the command line works. Running it inside your editor, in real time, while you type… that changes how you write code.
Real-Time Feedback Loops
Most modern editors support linting through plugins or built-in integrations. VS Code has extensions for ESLint, Pylint, Ruff, and dozens of others. JetBrains IDEs (IntelliJ, PyCharm, WebStorm) bundle their own inspections and support external linters too.
The JetBrains 2025 Developer Ecosystem Survey, based on responses from 24,534 developers, showed that VS Code and JetBrains IDEs remain the dominant editors. Both have deep linting integration built into their core workflows.
When a linter runs in your editor, violations show up as inline squiggly underlines the moment you write them. You see the problem before you even save the file.
Format-on-Save and Quick Fixes
Most editor integrations support format-on-save tied to linter rules. You write sloppy code, hit save, and the editor auto-fixes what it can: adds missing imports, removes unused variables, adjusts indentation.
This is where fast linters like Ruff really shine. If your linter takes two seconds to run, format-on-save feels laggy and annoying. Ruff finishes before you notice it started.
Quick-fix suggestions also show up as lightbulb icons or context menu options. Click one, and the editor applies the linter’s recommended fix inline. No terminal, no manual editing.
Why Editor-Level Linting Matters
The feedback loop is the point. When you see a problem within milliseconds of typing it, you fix it immediately while the logic is still fresh in your head.
When linting only runs in CI (after you’ve pushed), the feedback comes minutes or hours later. By then, you’ve moved on mentally. Going back to fix lint violations at that point feels like busywork, and developers start resenting the tool.
The best setup combines both: instant feedback in the editor for the developer, plus a continuous integration check that catches anything the editor missed. That way nothing slips through, and the developer experience stays smooth.
Linting in CI/CD Pipelines

Editor-level linting is for individual developers. Pipeline-level linting is for the team. Running a linter as a step in your CI/CD workflow means no code gets merged unless it passes the rules everyone agreed on.
The CD Foundation’s 2024 State of CI/CD Report found that 83% of developers now participate in DevOps-related activities. Linting is one of the first automated checks most teams add when setting up their pipelines.
How Teams Run Linters in CI
The setup is straightforward. You add a step to your pipeline config that runs the linter against the code in the pull request. If the linter exits with errors, the build fails, and the merge is blocked.
Common CI platforms for this:
- GitHub Actions (the most popular for open-source and small teams)
- GitLab CI
- Jenkins
- CircleCI
GitHub Actions saw massive growth in 2025, with its architecture refresh allowing the platform to handle 71 million jobs per day and reducing queue times by 62%, according to a 2026 tutorial by Tech Insider.
Linting Only Changed Files vs. Full Scans
Running the linter across your entire codebase on every pull request works fine for small projects. For larger ones, it gets slow.
Most teams configure their pipelines to lint only the files that changed in the current commit or PR. This keeps feedback fast, usually under a minute, even on big repos.
Full codebase scans still run, just less often. Usually on a nightly schedule or as part of a weekly quality assurance sweep.
Combining Linting with Formatting Checks
A typical pipeline runs both a linter and a formatter as separate steps. The formatter catches style-only issues. The linter catches everything else.
| Stack | Linter | Formatter | Pipeline Behavior |
|---|---|---|---|
| JavaScript/TS | ESLint | Prettier | Block merge on either failure |
| Python | Ruff or Flake8 | Black or Ruff | Ruff handles both in one pass |
| Go | golangci-lint | gofmt | gofmt runs automatically, lint is separate |
| Ruby | RuboCop | RuboCop | Single tool handles both |
Airbnb runs ESLint with their own shared config across their JavaScript projects. Their style guide config is one of the most downloaded ESLint presets on npm, which gives you an idea of how seriously large engineering teams take pipeline-level lint enforcement.
Linting vs. Formatting vs. Static Analysis
These three tools get confused constantly. They overlap a little, but they do different things. Using all three together is common, and the boundaries matter when you’re deciding what belongs in your deployment pipeline.
What Formatters Do
Formatters only care about how code looks. Indentation, spacing, line length, bracket placement, trailing commas. They don’t analyze logic or catch bugs.
Prettier (for JavaScript, TypeScript, CSS, HTML) and Black (for Python) are the two biggest. Go ships with gofmt built into the language toolchain.
Prettier’s own documentation says it clearly: “Use Prettier for formatting and linters for catching bugs.” The ESLint team agrees. They’ve publicly said that combining linting and formatting in one tool causes conflicts.
What Linters Do (That Formatters Don’t)
Linters check code for logical issues, suspicious patterns, and rule violations that go beyond appearance.
- Unused variables and imports
- Accidental global variable declarations
- Missing break statements in switch cases
- Calls to deprecated functions
- Potential security issues (like using
eval())
Some linters also handle formatting rules, which is where the confusion starts. But running both a dedicated formatter and a linter, with the formatter’s conflicting rules disabled in the linter, is the standard approach on most professional projects.
Where Static Analysis Goes Deeper
Static analysis tools like SonarQube, Semgrep, and Coverity operate at a different level entirely. They track data flow across functions, detect security vulnerabilities, find race conditions, and identify complex bug patterns that no linter would catch.
| Tool Type | Focus | Speed | Depth |
|---|---|---|---|
| Formatter | Code appearance only | Very fast | Surface-level |
| Linter | Style + logical patterns | Fast | Medium |
| Static analyzer | Security, data flow, complexity | Slower | Deep |
Sonar’s research found that technical debt costs $306,000 per year for a project of one million lines of code, equal to 5,500 developer hours of remediation. Tools at all three levels (formatting, linting, and deep analysis) work together to keep that number down.
Most teams start with a formatter and a linter. Static analysis comes later, usually when the project grows large enough that security and complexity become real concerns.
How to Add Linting to an Existing Project

Adding a linter to a brand-new project is easy. Adding it to a project with 50,000 lines of existing code that never had lint rules? That’s where it gets tricky.
The mistake most teams make is turning on all the rules at once, seeing 4,000 violations, panicking, and either abandoning the effort or spending two weeks fixing everything. Neither is a good use of time.
Start Lenient, Tighten Over Time
Begin with a small set of rules, the ones that catch actual bugs rather than style preferences. In ESLint, that means starting with the “recommended” config and nothing else.
Practical sequence:
- Install the linter with a minimal config
- Run it once to see the baseline violation count
- Fix the easy wins (auto-fixable issues)
- Set remaining violations to “warning” instead of “error”
- Promote warnings to errors one category at a time over weeks or months
McKinsey’s 2022 study found that technical debt accounts for up to 40% of a company’s entire technology estate. Trying to fix all of it at once is how projects stall. Incremental linting adoption follows the same principle: small, steady improvements beat a big-bang cleanup.
Baseline Files and Legacy Ignores
Some linters support baseline files. A baseline captures all existing violations at a point in time and tells the linter to ignore them. After that, only new code gets checked against the full ruleset.
This is the most realistic way to adopt linting on a large, mature codebase. Nobody has to stop shipping features to clean up old violations. New code stays clean from day one. Old violations get fixed gradually as developers touch those files for other reasons.
Sonar calls this approach “Clean as You Code.” Their research backs it up: over a five-year period, technical debt costs for one million lines of code can reach $1.5 million. Preventing new debt from entering is more cost-effective than trying to pay down all the old debt at once.
Getting the Team on Board
Rules without buy-in don’t stick. Before enforcing anything in CI, run the proposed config locally across the team for a week or two. Collect feedback. Adjust.
Stripe’s Developer Coefficient report found that developers already spend roughly 13.5 hours per week managing technical debt. Adding a linter that creates more friction than it removes will make that number worse, not better.
The goal is to make the linter feel like it’s helping, not policing. That means picking rules the team actually agrees with, not rules some blog post told you to use.
When Linting Creates More Problems Than It Solves

Linting is valuable. It’s also annoying when done wrong. And a lot of teams get it wrong.
The problems usually come from the same few patterns: too many rules, too strict too early, and rules that nobody on the team actually cares about.
Rule Fatigue and False Positives
Too many warnings train people to ignore all warnings. This is the biggest risk with linting.
If your linter flags 200 issues per file and half of them are style nitpicks that don’t affect behavior, developers start mentally filtering out the output. The real bugs hiding in that noise get overlooked.
A 2019 study by Chalmers University of Technology found that developers waste on average 23% of their working time due to technical debt. Ironically, an overly aggressive linter config can add to that waste instead of reducing it.
Bikeshedding Over Style Rules
Tabs vs. spaces. Trailing commas or not. Semicolons in JavaScript.
These debates have burned more meeting hours than anyone wants to admit. The fix is simple: pick a popular preset (Airbnb, StandardJS, Google’s style guide), accept that it won’t match everyone’s preferences, and stop discussing it.
Prettier was built specifically to end these arguments. It’s opinionated on purpose. Your team can disagree with its choices, but at least they’ll disagree consistently. That was the whole idea behind how James Long designed it in 2017.
Performance Overhead on Large Codebases
Slow linters kill adoption. If your linter takes 30 seconds to run on save, people turn it off.
This is the exact problem that drove Ruff’s creation. Pylint running against a 250,000-line Python codebase takes minutes. Ruff does it in under a second. The difference is not just speed. It changes whether developers actually keep the tool running.
| Scenario | Risk | Fix |
|---|---|---|
| Too many rules enabled | Developers ignore all output | Start with “recommended” only |
| Style-only rules in error mode | Blocked merges over cosmetic issues | Use warnings for style, errors for bugs |
| Slow linter in editor | Developers disable the plugin | Switch to a faster tool (Ruff, oxlint) |
| Custom rules nobody maintains | Rules go stale, produce false positives | Assign an owner to each custom rule |
The Cost of Maintaining Custom Rules
Custom linting rules are great until the person who wrote them leaves the team. Or until the internal API they enforce changes and nobody updates the rule.
Stale custom rules produce false positives. False positives erode trust. And once your team stops trusting the linter, you’ve lost most of its value.
If you write custom rules, document them. Put them in source control alongside the code they check. And assign someone to maintain them, just like you would for any other piece of your development process.
FAQ on What Is Linting In Programming
What does linting mean in programming?
Linting is the process of running a tool called a linter against source code to detect errors, style violations, and suspicious patterns. It’s a form of static code analysis that checks your code without executing it.
Where did the term “lint” come from?
Stephen C. Johnson created the original lint tool at Bell Labs in 1978 for the C language. The name references clothing lint, small unwanted fragments that a lint trap catches.
What is the difference between a linter and a compiler?
A compiler checks if code is syntactically valid and can run. A linter checks for likely bugs, style problems, and coding standard violations. Compilers allow technically valid but problematic code that linters would flag.
What is the difference between linting and formatting?
Formatting tools like Prettier only fix how code looks (indentation, spacing, line length). Linters like ESLint also catch logical issues: unused variables, deprecated function calls, and potential runtime errors.
Which linter should I use for JavaScript?
ESLint is the standard. It gets over 110 million weekly npm downloads and supports JavaScript, TypeScript, CSS, and HTML through plugins. Companies like Microsoft and Netflix use it across their projects.
Which linter should I use for Python?
Ruff is the fastest option, running 10 to 100 times faster than Flake8 or Pylint. It replaces multiple tools in a single pass. Pylint and Flake8 still work well for teams that prefer established options.
Can linting fix code automatically?
Yes. Most modern linters support auto-fix for certain rules. ESLint’s --fix flag and Ruff’s autofix model can remove unused imports, add missing semicolons, and rewrite outdated syntax patterns automatically.
Should I run a linter in my CI/CD pipeline?
Absolutely. Running a linter as a build step in GitHub Actions, GitLab CI, or Jenkins blocks merges that violate your team’s rules. It catches anything developers missed in their local editor setup.
Does linting slow down development?
Not with modern tools. Ruff lints entire codebases in under a second. ESLint runs in real time through editor plugins. Slow linters exist, but the current generation is fast enough to run on every file save.
Is linting necessary if I use TypeScript or a strongly typed language?
Yes. Type systems catch type errors, but linters catch different problems: unused variables, style violations, deprecated API usage, and code quality patterns that no type checker looks for.
Conclusion
Understanding what is linting in programming comes down to one thing: catching problems early. Whether you’re running ESLint on a JavaScript project or using Ruff to scan Python files in under a second, the goal stays the same. Find errors before they reach production.
The tooling has gotten fast enough that there’s no real excuse to skip it. Linters plug into your code editor, run inside your CI/CD pipeline, and auto-fix dozens of common issues without manual intervention.
Start with a recommended config. Add rules gradually. Let the formatter handle style and the linter handle logic. Your team’s code quality improves quietly, one commit at a time.
The setup takes minutes. The payoff lasts the entire life of your project.
- What Is Agentic Coding? The Next AI Dev Workflow - April 10, 2026
- From Setup To Monitoring: Why A DMARC Service Matters - April 10, 2026
- 4 Scalable Hosting Providers for Growing Small Business Websites - April 9, 2026







