Let me start with a confession: I once spent three hours debugging a deployment failure only to discover that our overzealous ESLint configuration was rejecting perfectly valid code because someone had the audacity to use a console.log
statement. Three. Whole. Hours. That’s when I realized we might have a problem.
Don’t get me wrong – I’m not advocating for the wild west of programming where semicolons are optional suggestions and indentation follows the chaos theory. But somewhere along the way, our development community has transformed code linting from a helpful assistant into a tyrannical overlord that micromanages every keystroke.
The Linting Industrial Complex
Code linting tools analyze source code to identify programming errors, enforce style consistency, and detect potential issues. They parse your code, construct an abstract syntax tree (AST), and check it against predefined rules. Sounds reasonable, right? Well, it would be if we hadn’t collectively lost our minds about it. The modern development workflow has become a gauntlet of linting tools. We have ESLint for JavaScript, Pylint for Python, RuboCop for Ruby, and the list goes on. Each comes with hundreds of rules, and somehow we’ve convinced ourselves that enabling every single one of them makes us better developers. Here’s a typical modern JavaScript project setup:
// .eslintrc.js - The monster configuration
module.exports = {
"env": {
"browser": true,
"es2021": true,
"node": true
},
"extends": [
"eslint:recommended",
"@typescript-eslint/recommended",
"plugin:react/recommended",
"plugin:react-hooks/recommended",
"plugin:jsx-a11y/recommended",
"plugin:import/errors",
"plugin:import/warnings",
"plugin:import/typescript"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": 12,
"sourceType": "module"
},
"plugins": [
"react",
"@typescript-eslint",
"jsx-a11y",
"import",
"react-hooks"
],
"rules": {
"no-console": "error",
"no-debugger": "error",
"no-alert": "error",
"no-unused-vars": "error",
"prefer-const": "error",
"no-var": "error",
// ... 200 more rules because why not?
}
};
Look at this configuration file – it’s longer than some of the actual source files in your project! We’ve created a secondary codebase just to tell us how to write our primary codebase.
The Productivity Paradox
Here’s where things get interesting. Linting tools are supposed to improve productivity by catching errors early and maintaining consistency. But in practice, excessive linting often achieves the opposite. Let me paint you a picture of a typical development session with aggressive linting:
// You write this beautiful function
function calculateTotal(items: Item[]): number {
let total = 0;
for (let i = 0; i < items.length; i++) {
total += items[i].price;
}
return total;
}
// But your linter has other plans:
// ❌ Error: Prefer for-of loop over traditional for loop
// ❌ Error: Variable 'i' should be named more descriptively
// ❌ Error: Consider using reduce() for array operations
// ❌ Error: Missing JSDoc comment for public function
// ❌ Error: Function should not exceed cognitive complexity of 5
So you spend the next 20 minutes refactoring a perfectly readable function to appease the linting gods:
/**
* Calculates the total price of items in the array
* @param items - Array of items with price property
* @returns The sum of all item prices
*/
function calculateTotal(items: Item[]): number {
return items.reduce(
(accumulator: number, currentItem: Item): number =>
accumulator + currentItem.price,
0
);
}
Congratulations! You’ve transformed a function that any junior developer could understand into something that requires a PhD in functional programming. Your linter is happy, but your future self (and your teammates) will curse you when they need to debug this at 2 AM.
The False Prophet of Code Quality
The linting community has somehow convinced us that more rules equals better code quality. This is like saying that a recipe with more restrictions automatically produces better food. Sometimes the best dishes come from breaking the rules, and the same applies to code. Consider this Python example that would make most linters have a nervous breakdown:
# "Bad" code according to linters
def process_data(data):
result = []
for item in data:
if item.is_valid:
processed = item.value * 2
if processed > 100:
result.append(processed)
return result
# "Good" code according to linters
def process_data(data: List[DataItem]) -> List[int]:
"""
Process data items and return values greater than 100.
Args:
data: List of DataItem objects to process
Returns:
List of processed integer values greater than 100
Raises:
ValueError: If data contains invalid items
"""
return [
processed_value
for item in data
if item.is_valid
and (processed_value := item.value * 2) > 100
]
The “improved” version uses the walrus operator (:=
) which is trendy but makes the code less readable. We’ve also added a docstring that’s longer than the function itself. Sure, it’s more “professional,” but is it better? That depends on your definition of better.
When Linting Becomes Counterproductive
Let’s talk about the elephant in the room: linting fatigue. When developers are constantly battling their tools instead of solving actual problems, something has gone wrong. Here’s a decision tree that illustrates when linting stops being helpful:
The problem becomes severe when teams spend more time configuring linters than writing actual features. I’ve seen pull requests where 90% of the comments are from automated linting tools complaining about spacing, variable names, or cognitive complexity scores.
The Configuration Nightmare
Each linter comes with its own configuration syntax, and keeping them in sync across team members becomes a full-time job. Here’s what a typical project structure looks like:
my-awesome-project/
├── .eslintrc.js
├── .eslintignore
├── .prettierrc.json
├── .prettierignore
├── .stylelintrc.json
├── .commitlintrc.json
├── .markdownlint.json
├── .editorconfig
├── pyproject.toml
├── .flake8
├── .pylintrc
└── tslint.json (deprecated but still there)
We have more configuration files than actual source code directories! Each tool has its own opinion about how things should be done, and they don’t always agree. ESLint wants single quotes, Prettier prefers double quotes, and your team lead has strong feelings about both. The worst part? These configurations drift over time. What started as “let’s enforce some basic consistency” becomes an archaeological dig through layers of accumulated rules that nobody remembers adding.
The Human Cost
Beyond the technical issues, excessive linting has a human cost that we rarely discuss. Junior developers, especially, suffer under the weight of overly strict linting rules. Instead of learning to write good code through practice and mentorship, they learn to write code that appeases automated tools. I’ve mentored developers who were afraid to write a simple loop because they weren’t sure which of the seventeen approved iteration patterns would satisfy the linter. They’d spend more time researching linting rules than understanding the business logic they were implementing. This creates a generation of developers who prioritize rule compliance over code clarity, who can recite ESLint error codes but struggle to explain what their code actually does.
A Practical Approach to Sane Linting
Now, before you think I’m advocating for complete anarchy, let me clarify: linting has its place. The key is finding the sweet spot between helpful automation and oppressive micromanagement. Here’s my battle-tested approach to linting configuration:
Start with the Essentials
Focus on rules that prevent actual bugs, not stylistic preferences:
// A sane ESLint configuration
module.exports = {
"extends": ["eslint:recommended"],
"rules": {
// Prevent actual bugs
"no-unused-vars": "error",
"no-undef": "error",
"no-unreachable": "error",
// Prevent dangerous patterns
"no-eval": "error",
"no-implied-eval": "error",
// That's it. Really.
}
};
Use Formatters, Not Linters, for Style
Style consistency is important, but it shouldn’t block your development flow. Use dedicated formatters like Prettier that automatically fix issues rather than complaining about them:
{
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5"
}
Set up your editor to format on save, and you’ll never think about spacing again.
Implement the “Two-Week Rule”
Whenever you’re tempted to add a new linting rule, wait two weeks. If you haven’t encountered the problem the rule solves during that time, you probably don’t need it.
Make Rules Warnings, Not Errors
Most linting violations shouldn’t block deployments. Convert aggressive “error” rules to “warning” status:
"rules": {
"complexity": ["warn", 10],
"max-lines-per-function": ["warn", 50],
"no-console": "warn" // Because sometimes you need to debug
}
The Future of Development Sanity
The industry is slowly recognizing that developer experience matters. Tools like Ruff for Python are focusing on speed and simplicity rather than maximum rule coverage. This is a step in the right direction. We need to remember that linting tools are meant to serve us, not the other way around. A linter that improves code quality while staying out of your way is worth its weight in gold. A linter that turns every coding session into a battle of wills needs to be reconsidered.
Finding Balance
The goal isn’t to eliminate linting – it’s to use it judiciously. Your linting configuration should be like a good suit: tailored to fit your team’s needs without restricting natural movement. Consider these questions when evaluating your linting setup:
- Does this rule prevent actual bugs? If not, consider removing it.
- Would a new team member understand why this rule exists? If you can’t explain it in one sentence, it might be too complex.
- Are we spending more time configuring tools than solving problems? If yes, you’ve gone too far.
- Do our linting rules reflect our team’s actual coding standards? Rules that everyone ignores or works around are worse than no rules. Remember, the best code is code that works, is maintainable, and can be understood by your teammates. If your linting setup supports these goals, great. If it becomes an obstacle to them, it’s time to reassess.
Conclusion
Code linting, like hot sauce, is best used in moderation. A little bit can enhance the flavor of your codebase, but too much will overwhelm everything else and leave you with tears in your eyes.
The next time you’re configuring a linter, ask yourself: “Am I solving a real problem, or am I just adding rules because I can?” Your future self, your teammates, and your sanity will thank you.
And if you’re still spending three hours debugging linting configurations instead of shipping features, maybe it’s time to step back and remember why you became a developer in the first place. Spoiler alert: it probably wasn’t to achieve perfect semicolon compliance.
Now, if you’ll excuse me, I have some console.log
statements to add to my production code. Don’t tell my linter.