Linters and formatters (henceforth referred to as “silly tools”) make your code worse, and they make you a worse coder.
They encourage you (sometimes very insistently) to pay less attention to detail, in a field where attention to detail is all-important. If you’re using a linter and/or formatter, you’re ceding an important part of your job to a piece of software which can never do it as well as you could.
Linters and formatters treat people who are by definition highly-skilled knowledge workers as if they’re incapable of making a sensible decision for themselves.
They treat arbitrary decisions made by some guy 20 years ago based entirely on (sometimes empirically incorrect [details more]) opinion as if they were inviolable rules. It is not a fucking “error” if I don’t put whitespace around an operator, or leave whitespace at the end of a line, and I find it offensively inaccurate and needlessly alarmist that you would choose to mislabel it as an “error” – as far as I’m concerned, you labelling this as an “error” deletes all your credibility with regard to code style: It’s not an error unless the code doesn’t run, or does something unexpected.
Note the “why is this bad” section on the ruff rules page above. Ruff actually stands out amongst the crowd of dreck here, because it actually bothers to have a “why is this bad” section, unlike many/most others – others wouldn’t deign to offer up an explanation as to why this thing being labelled as an “error” is bad, instead they just expect you to simply blindly accept the incorrect opinion of some guy 20 years ago and not question why this is a thing. So, kudos, I guess, to ruff, for bothering to attempt an explanation, even if said explanation is woefully inadequate.
But I got sidetracked – note the “why is this bad” section, which basically just says “because pep8 says so”, giving zero technical justification at all. When you click on the link to pep8 provided, it takes you to the “pet peeves” section, which is literally just a list of things that Guido Van Rossum has decided he doesn’t like for his own personal style.
Importantly: at no point did Guido say that I should use this style (ok, fine, the language being used is “you should do X”, but despite that language this is only a recommendation / opinion: the document itself starts out by saying “here are some recommendations, you shouldn’t blindly follow them”). This is just Guido’s preference.
But, due to misinterpretations by stupid people of the document’s intent, we now have a bunch of stupid automated tools wasting my time proclaiming that it’s an “Error” that I don’t use the exact same style that Guido likes, enforcing a bunch of things which are not laws as if they were, and trying to dogmatically fit the style for all code into this list of recomendations, opinions, and general guidelines which were never intended to apply everywhere.
This is the fault of the people implementing and using linters, not of Guido or pep8. Granted, the language in pep8 could have been more clear as to its intention, and less prescriptive, but personally I think it’s fair to expect knowledge workers to read between the lines and question things.
Code is a form of art. There’s no style that is universally applicable or good in all circumstances. Similarly there’s (very close to?) no style which is unambiguously bad and should never be used in any circumstances. Correctly-formatted code should be a thing of beauty, self-consistent and easy for a human to parse. This is not something that can be encoded as a set of rules. At best you can come up with guidelines for things you should probably do most of the time in most normal circumstances. Which is exactly the intent of pep8.
The very first section of PEP8 after the introduction is called A Foolish Consistency is the Hobgoblin of Little Minds. It states:
sometimes style guide recommendations just aren’t applicable. When in doubt, use your best judgment. Look at other examples and decide what looks best
They’re stupid tools: For me personally, linters give a greater than 99% false positive rate. I.e for me, MORE THAN 99 times out of a hundred, the linter’s warning is simply incorrect and should be ignored.
Linters and formatters litter your code with trash comments (# noqa
is my poster child for this, but there’s a bunch, depending on which silly tools you’re using. #type: ignore
is another one) that shouldn’t be there – directives for the linter to follow (“hey linter, actually I really did mean what I typed here, and I actually know what I’m doing and it’s not a problem, even though you insist it’s an ‘error’, so STFU”). This affects the readability of your code by littering it with useless trash, which encourages both you and other developers to not read comments. Perhaps this has a connection to the ill-advised current fashion of not commenting anything ever.
Don’t even get me started on typing and static type checkers for dynamic languages. What you are trying to do is fundamentally impossible and broken – if you want static typing, may I suggest using a statically-typed language? There are tons of them.
But I’m totally smart! And I like linters!
How many projects have you run which didn’t use one?
Is it zero? Because if it’s zero, you don’t actually know what you’re gaining and losing by using a linter. Which means you’re not qualified to have an opinion.
No I totally tried it for real-reals! and my codebase became really ugly and messy and inconsistent really fast!
You lack discipline.
This is not a reason why linters are good, it’s a reason why you are shit. Being self-consistent in your style really really isn’t difficult to do.
And if you can’t even be self-consistent in your coding style, why would anybody expect you to be consistent in complicated things like your object model or interface design?
They wouldn’t. Because you wouldn’t. And that’s why your API needed to be versioned rather than simply doing things in a backwards-compatible way, because you had to fundamentally refactor some huge part of your system, because you didn’t bother to take a few minutes to think about that design before you started pumping out substandard code.
In other words: your linter has made you worse at what you do by encouraging laziness. To get better, I would recommend that you stop using linters as a first step, and work on your own style and maintaining consistency in both your style and engineering. I’d also recommend that you do some reading about critical thinking and why it’s important.
They’re stupid tools for stupid people: stupid because they don’t see how futile and pointless and broken linters are. If you were not-stupid enough to understand these things, you wouldn’t be using a linter.
But I’m totally smart! I wrote my own linter!
No, you’re not smart. Regardless of how god a coder you are and how amazing your code is, you’ve skipped a fundamental piece of critical thinking very very early on in the process – one which a smart person would definitely not have skipped – asking the question: “Is there a reason for this software to exist?”. The answer is no. You’ve not considered this, and have proceeded anyway, burying god knows how many hundred hours into building your fundamentally-broken software. A smart person would have said “hey, this is a fundamentally stupid idea, rather than writing this software I’m going to do nothing at all”.
You might be a great coder, and maybe even a really smart one. But you’re not a smart person if you think linters are a good idea.
They’re stupid tools for stupid people by stupid people: You could have spent your time writing literally anything else. Or playing Tetris. Or staring at a wall. Instead you chose to write a linter. QED.
Are there any positives?
For junior coders, maybe. It’s probably worth having a way to scan through their code and see a bunch of recommendations before bothering the senior dev with it. But this should be an educational thing – it absolutely should not happen automatically and invisibly when they press “Save” in their IDE, because that teaches them nothing (other than to not pay attention or care about style very much because the code you wrote will just be automatically overwritten with machine-formatted trash anyway)
For everyone else, there are aspects of these tools which might be useful sometimes. Probably on an irregular basis. For instance I’d like to be able to have a piece of software spit out a list of all the imports it thinks aren’t being used, so that I can remove them. If it offered me some way to automatically remove them after I’ve checked whether they’re actually unused, that’d be a bonus. And if it was possible to run some of these rules on only the parts of the file I’ve modified to see “warnings” without having it take any action, then that might occasionally provide me with something useful. But not often enough for me to spend even 2 minutes setting that up.
One big barrier to these silly tools actually being useful is their refusal to provide an option to work on partial files – a dev should be able to run these silly tools on only the code they’ve modified, but this is fundamentally at odds with the oppressive way these tools want to ram their preferred style down your throat: they insist on “fixing” entire files all at once – the fact that ok=True
has already been committed into your codebase and used in prod for 10 years has no bearing in their mind as to whether it’s an “error” or not, the only thing that matters is that Guido wrote an opinion in pep8 nearly a quarter century ago.
A Road Forward
Let’s try to be constructive here: rather than just ranting about how the current thing is terrible and fundamentally broken, let’s try to actually paint a picture of how things should look:
- Stop trying to use static type checking for dynamic languages, because this is a fundamentally broken approach that only wastes time and makes your code less readable. Delete mypy and use nothing instead.
- Git commit hooks that run linters and formatters should go away. Now and forever.
- The silly tools need to be updated to work correctly with git, so that they can be run partially on a file. Juniors and people who can’t be bothered paying attention to detail should be able to run their silly tools on just the parts of the file they have updated, so that they are not imposing their stupid style on everybody else
- The silly tools need to come up with a way to store the
# noqa
type directives somewhere that isn’t in a comment in the actual source file. My suggestion would be a sidecar file. This will have implementation quirks and be much less straightforward to implement than you’d expect. I don’t give a shit – that’s your problem if you want to use these silly tools. But you need to get your stupid meaningless trash comments out of my beautiful, well-commented source code. - I’d suggest that a junior/lazycoder should be able to easily run their silly tools and see a summary of the messages it generates and the changes it proposes, with an option to accept or refuse each of these individually.
- Once every 6 months or so it would be nice for me to be able to easily run a tool over my entire codebase to find and offer me the option to remove unused imports
In Conclusion
So, there you have it: linters and formatters are stupid, the people who use them are stupid, and the people who wrote them are stupid. They should not be used because they make you worse.
I feel like I’ve only said about 10% of the things that could be said here, but this thing is already getting long enough and I’ve been at it for a while. Maybe I’ll come back to it or post another installment one day, we’ll see.
It’s possible that you take exception to a lot of the things I’ve said here.
That’s cool. Don’t stress. You’re just wrong, that’s all. The thing to do about it is simple: just uninstall your silly tools and move on with your life. It feels really great deleting that # noqa
trash from your code. I promise.