Linters: Why your team should tread lightly
The usage of linters, tools to analyse your code and tell you where it can be improved upon, is becoming more widespread in recent years. Many programmers nowadays start their careers in places where a CI system runs ESLint, Rubocop or Credo against all their checkins. Overall, this is a good thing, because it enables discussing code quality issues within teams more openly and in a transparent way.
(you can imagine there’s a “but”)
BUT, there is a dark side to this: I’ve seen my fair share of setups where a failing style check prevents a build from triggering the actual test stage. The implied sentiment towards those learning our craft is
If there is a space or comma violating our style guide, we are no longer interested in wether your code works or not.
Putting stylistic (some would say “cosmetic”) concerns before concerns regarding the validity of the code is disconcerting, because it reduces a team’s ability to judge the validity, correctness and readiness of their product.
I wish these cases were outliers. My mentor taught me that software has to satisfy two contraints:
- Your Software has to work (i.e. do the job it’s built for).
- Your software has to be maintainable by others (i.e. changing requirements should not necessitate a rewrite).
And to this day I think this is a good mantra to live by: Software that does not serve its purpose is useless (both from a pragmatic and a philosophical standpoint). Software that does its job and is hard to change puts us in a slightly better position, but not really.
The world around us is changing constantly and our tools should be able to adapt to these changes. We invented things like platform independence and cross compilation for this very reason.
The problem we are facing
Many senior programmers get angry when younger team members tell them something is “impossible” due to their framework of choice not having a template solution for the problem at hand. They assert (correctly) that engineers should not buy into a chosen framework’s limitations, but instead apply informed reasoning when thinking about solutions for any given requirement (because any framework is just a tool in the programmer’s hand).
With the rise of linters, however, people seem to forget that these are merely tools that should serve us. Instead, all too often, teams impose a set of arbitrary rules upon themselves and then build their software to satisfy these rules.
Suddenly, the linter is no longer a servant, but becomes the boss, a sovereign of sorts.
What the original problem linters are supposed to solve?
This is not a problem with engineering teams, but rather a systemic problem with organisations (esp. growing ones): The decision to have rules comes from a desire for alignment, coherence and teamplay.
But then the concrete rules are ofen introduced in response to very specific situations:
-
A teammate had to take a luxury sedan at the car rental place during a business trip because that was the only car available. Somebody gets a wind of this, without context, and pushes for a rule that car rentals on business trips should be limited to small cars only.
-
A client pushes for a sudden delivery, but half the team goes on vacation two weeks later. They push the release through, taking a shortcut here and there. The release is a success, but their engineering manager finds all these super long methods with lots of
TODO: fix/refactor this
comments. He imposes a linter rule that methods should be no longer than 20 lines of code.
In both of these situations someone reacts to a situation controlled by its context and imposes a rule lacking said context.
So, what should we use linters for?
As a tool, a compass and guidance for discussion.
A linter is a bad sovereign, especially when developing your own style because the standard rules, often times referred to as “community best practises”, limit both your playing field and the paths you an explore on. At best, they prevent some common errors which you would soon learn to avoid yourself. At worst, they are responsible for neglecting the overall architecture of your program. I mean, the linter is satisfied, so the program is done, right?
If we focus too much on “there has to be a space after the comma” then we should not be surprised by the architectural deficiencies in our programs (which can ironically not be found by a linter).
This is why a linter does not provide a standard for good software. Good software is not a result of a green linter stage. Good software is a result of people being considerate. If you change working software time and time again until the linter finally gives its “okay”, then you will probably turn a piece of focused, problem-oriented code into a formatting- and code-style-oriented mess.
Finally, a linter can not replace a mentor, not even a bad one. You can not interact with it, it just screams at you.
But why is that?
Most, maybe all, linters are static tools used inside a context without knowing any context themselves.
- it does not know anything about the programmers experience
- or the communication culture in a team
- or about challenging clients
- or deadlines
- or shitty, but necessary bugfixes made in a hurry
Those are the things I myself have to acknowledge (or somebody on my team with the authority to do it, like a team lead or other senior person). But a person has to use tools like linters as windows into their production process, into their process of making the software. They can use them as gauges, as instruments which they can base independent decisions upon.
Nobody should base their whole approach to designing a codebase on a linter config.
Take away this picture: If I am wrinting a text in Microsoft Word, I oftentimes get those red, blue and green squiggly lines. Most of the times they are right and I made a mistake, but every once in a while it’s just a technical term which the spell checker does not know.
And then I can make the decision to ignore the suggestion. Everybody can do that.
And we should treat linters the same way we treat Word’s spell checker.