Linting is controversial. Linting is the cause of many debates. Linting can be one of your most powerful tools, if done right. If you’ve spent any time in the front-end development ecosystem, you’ve most likely heard the word “linter” or “linting”. My definition of linting is, “The systematic application of preferential best practices in coding,” with a heavy emphasis on “preferential”. The easiest block of linting rules to justify are those which relate to the prevention of unexpected bugs through ambiguity, such as nested scope declaration of variables. This block of rules addresses coding techniques which teams will unanimously call wrong.
For me, the next block of rules surrounds version control diff management. These rules are opinionated, but in my view, carry a bit more logic. For example, we might consider the notorious example of tabs versus spaces, carriage returns, or even trailing commas. Getting buy-in on this block isn’t too hard as they don’t affect the day-to-day life of developers.
The block of rules I’m talking about today though are those rules which lean heavily into the preferential category. This can be something like destructuring versus dot notation. Whatever justification used for the rule can be used against the rule. The simple enforcement of these rules alone adds zero value to your code base. The case I’m making though is the value added by making the cognizant choice to define preferences and capture in enforced linting rules.
Preferential linting rules can feel repressive to the individuality of developers, but it’s important to understand why we implement such a strict set of rules – consistency. Consistency reigns supreme here. When you open any given file, it should feel familiar and easy to understand, even if you’ve never worked on that feature or portion of the code base. There shouldn’t be hints as to which developer wrote the code. Again, this may be starting to sound like a repressive regime, but that’s not the goal. We’re not looking to repress how developers go about solving problems and implementing solutions. We’re instead looking to bring consistency to the syntax leveraged in those given solutions. Consistency means that developers aren’t stuck trying to interpret a file or confused about syntax they’ve never seen before. Consistency means that new developers can quickly onboard and contribute across your code base. Consistency means that files written a year ago by an entirely different team feel identical to the new team just refactoring them. Maintaining true consistency is hard. It can only be achieved through a strict set of linting rules. Leaving gaps or ambiguity opens the door to inconsistency.
The hardest hurdle on your way to strict linting is getting initial buy-in. If you have an existing code base and developers accustomed to a certain way of coding, you’ll find a lot of pushback. In these scenarios though, it may be best to hand the task over to your developers to work through defining these rules. Setting up a steering committee tasked with defining linting rules has been a favorable approach, as it ensures everyone’s voice is heard.
The other major challenge of working in an existing code base is the sudden introduction of numerous new rules triggering thousands, if not tens of thousands of errors. I’ve created some techniques to solve this issue which I’ll discuss in a future article. For new code bases, now is the time to go all out on linting rules. You’ll find it much harder to retroactively implement stricter rules and developers will be coming into a fresh code base where all of the rules have already been defined. With either approach though, listen to your development teams. Give them the tools to modify rules, such as a steering committee. These rules should be strict and deliberate, but they should not be set in stone.
Linting CSS (or SCSS/LESS) serves to not only maintain consistency in your code, but in the visual appearance of your application. Defining strict linting rules for CSS allows you to prevent design inconsistencies such as the use of unauthorized colors, measurement units, or even the level of selector nesting seen in precompiled instances such as SCSS. Linting CSS also aims to prevent unexpected bugs by identifying mismatched attributes or those attributes which have no applicability based on element or superseding attributes.
There’s also a strong case for linting HTML nowadays. While less applicable, linting HTML can ensure you’re properly structuring elements in a way which does not violate any permissible nesting rules. We can also configure this to monitor ADA compliance violations. If you’re leveraging JSX, this functionality can even be baked right into ESLint through a11y support.
Why not Prettier?
Prettier is opinionated – that’s their entire philosophy. After all, that’s what I’m preaching so why wouldn’t I suggest Prettier? For me, it comes down to granularity and control. As I mentioned before, rules should be strict and deliberate, but they should not be immovable. Developer consensus should carry enough weight to override or modify rules. Prettier only allows this on a small scale. In my quest for consistency, one could argue that simply using Prettier ensures a consistent set of expected rules, even for developers just joining your organization. As the Prettier rules are nearly the same in any implementation, you might be able to expect incoming developers to have already mastered the ruleset. For me though, the lacking granularity of Prettier does not justify the case for consistency.
Be lazy, automate everything
Implementing linting rulesets is just half of the process. These rules should be configured to fail builds if they are not met, but our goal is to simplify the lives of our developers. If we can reduce their time spent refactoring just to satisfy a bunch of linting rules, it will ensure acceptance of strict rulesets. Most linters provide automatic resolution of discernable errors. We can simply capture this in an npm script which developers execute before committing code. This script resolves all errors which it is capable of discerning. Those remaining are returned as errors in the console. We can take this one step further though and automate this process through Husky. By setting up a pre-commit hook, we can lint our code before committing anything. During this process we’ll attempt to correct any linting errors found. If we are unable to resolve errors, we’ll fail the commit, ensuring only well-formatted code is entering our code base.