Some time ago I assist a junior developer, which has an apparent issue on him code, to find out what was wrong on it. He installed a lint and downloaded (god know why) a customized dictionary for it. I prepared a little test environment and showed him the difference between different tests, at different points and some limitations of each (on this little environment).
Recently I was talking about testing with a colleague and I remembered this anecdote and told myself to write a post about that as it could be interesting for many.
What is linting?
Linting is the automated checking of your source code for programmatic and stylistic errors. This is done by using a lint tool (a.k.a. linter). A lint tool is a basic static code analyzer.
The term linting originally comes from a Unix utility for C. There are many code linters available for various programming languages today.
Lint programming is a type of automated check. It can happen early in development, before code reviews and testing. That’s because automated code checks make the code review and test processes more efficient. And they free your developers to focus on the right things.
Many automated testing tools take advantage of a built-in linter to throw errors, set some score about code quality and block the compiling or a deploy stage if this score is lower than the desired (automatically or pausing the process throwing a notification to manually check if this "errors" should be patched or not).
What is intended for?
Linting is intended to reduce errors and improve the overall quality of devs code, accelerate development and reduce costs by finding errors earlier.
When to use Lint software?
If this is new for you, you may be some kinda hyped with lints at this point but they are not a programagically tool. A lint is simply a "scrapper"/file reader script with a file (usually a json or xml) with predefined (or customized) rules about how the code must look like. In fact IDEs have a built-in Lint which will throw you an error (i.e. if you call a non-existent method, or if you try to add a string value into an integer variable) or a warning (i.e. if you declare a variable inside a conditional and then you return that variable at the end of your method). But I'm not talking about the built-in code lint, as this linters are "soft" and permissive, otherwise many people won't use those IDEs, we will see why later.
You may use a specific Lint if you fit into this statements:
When You Use Interpreted Programming Languages.
Some languages are better suited for code linting than others. PHP, Python and JavaScript are interpreted languages, this means they lack a compiling phase*. So using lint software is effective for ensuring consistent coding style and resolving basic coding errors in these cases.
But, when it comes to compiled languages, such as C and C++, using lint software might not be enough. C and C++ are complex and may require more advanced code analysis.
*You may be shouting I compile JavaScript! but it's in fact, not true. Nowadays on front end we are using tools like bundlers, adding some javascript tools that pass our js by some phases such as minify, ugglify, ofuscate. Some of these tools also delete unused code on its output. But none of this things means you are compiling javascript, as the output will be a .js file anyway; no bytecode, no assembler.
It's true that you can convert javascript into web assembly but it's not a direct path from one to another; you must convert your js into something "compilable" such C code and then compile it into web assembly.
When You Use Standard Rules
This is a bit tricky statement. On a programming language you will not be able to use something that is not built-in on your programming language (that is not true at all, as you can add libraries or throw system commands from programming languages, but anyway Lint have nothing to do with that so...)
What Standard Rules stand for?
Using a framework will fit nice here, you must use framework built-in methods to reach the desired output.
Sometimes standard rules means fashionably code. This methodologies and workarounds that community repeat like a mantra till they find another fashionably way to reach the target of a given task, or till someone with public repercussion tells the community "hey, I was analyzing this workarounds and I find that there's a better way to reach this for this reasons" and then the community moves on.
When Your Needs Are Basic
Lint tools are great for basic analysis. But if you need more sophisticated analysis you must look elsewhere (automated testing tools and test-driven development).
Where should I see a lint?
1. On automated tests (we call them automated because they run automatically but someone needs to code the test for each use case on an app) such as Cypress, you can add a lint to score the code quality.
This can run on development time (when the dev hits the test button) or likely on the deploy stage. For example, when you push something into master branch it could be a trigger to raise the tests, including the lint, and block the deploy (and blame you) if you did something wrong, then you will need to repair or find the bug (this doesn't mean it's your fault. I could push an API that makes your client crash receiving unexpected values, and this will be my fault, not yours).
This tests are not intended to blame you but to help into the development process, it's usually better to find a bug before integrating into pre-production stage due to a test failure than finding it by surprise into production.
2. On big projects, usually on big companies. If you are on a very big company the project manager should like all the code look like the same (formatting) so they probably have a lint for it somewhere that point you if you added 4 white spaces to indent something they want with 2 and that will blame you if you didn't declare a method with camel case for example.
I find tones of junior devs trying to fight against that because they like some particular formatting, but without this tools, this big software apps will look like a frankenstein monster on a future increasing the difficulty on reading all parts or following some method paths and class overrides. Think on it like reading a book on where each paragraph have a different font-size, font-family and so.
Where not to use it?
This point is so easy: Don't use it on dynamic context and on non-programming languages.
1. The first one is easy to understand. If you are on a dynamic context the lint (a static checker) will never know the final result and will blame you for things that are OK, I'll add some example on the next point.
2. This should sound ridiculous, redundant or you can tell me "Where you use a lint if it's not on a programming language?"
Mulder, there are HTML and CSS linters out there.
Yea.. um... But, how?
Well, they are not linters in truth, they are only code-checkers, the same you can do with the w3c validator, but instead on copying your code into a validator, they usually run on development time blaming you for different things based on someones opinion, which is annoying, inaccurate and really does not fit nowadays usage of this two technologies (or three if you add SCSS), and most important, they usually don't have a reason for each statement, or the reasons contradict from a point to another, or the reason is outdated, or the reasons are not applicable to all projects. That's why you will find tones of "Forget about CSSLint, it's opinionated and does not fit your project" comments and many similar others across the internet.
You can find some pieces of code that are called Linters for some reason, but at least they tell you "I'am an opinionated code formatter" like this one .
HTML lint could be useful sometimes as it will force you to add an alt attribute to the images and other accessibility and good practices (Most IDEs shows you a warning for it too without adding a lint, which is better) but this will not prevent you from using a <span> as a <p> or other bad html usage.
It also will annoy you if you are writing template components with silly statements like "you don't have doctype declaration, you don't have html, head, title nor body tag. And you could thing "of course, as this file will be loaded inside a document which already has this tags" but HTML lint can't know it; Lints are static checkers and you are working on a dynamic environment, so move on and delete html lint at all.
CSSLint is a bit different, there's no thing you can lint here anyway, you can write valid or invalid CSS but that's all.
There are good practices on CSS that stand for efficiency and speed purposes (it will also benefit to UX), others that will go for maintainability, scalability and readability purposes (usually based on development experience), another ones stand for accessibility (based on UI/UX studies and browser renderings, screen contrast tests and so). But there's no point on combine it all into a linter and telling devs to match all those practices, first of all because there are contradictions that needs a human to make the decision to use one or another.
An use case where to choose could be dealt by the fact that adding all accessibility CSS code will observably increase the size, loading time, and first time interactive metrics so you usually will choose only the accessibility rules that fits best to your customers while maintaining good performance.
For setting a silly example about an opinionated rule on the CSSLint let's say I want a linear-gradient as background on my homepage main block. CSSLint will throw a warning about linear-gradient performance.
At this point CSSLint want you to delete the linear-gradient but what are the alternatives?
Using an image will be tones of times less efficient for reaching the target design (and also will reduce the visual quality of the gradient, and will make it harder to resize to fit all posible sizes of the block).
So what can we do? Simply ignore the CSSLint, because it's opinionated and this is not supported by any reason.
There's a difference on adding a linear-gradient vs adding 42342 linear-gradients on the same view. By the way if you need them, it will still be more efficient than using images.
You also can find contradictory statements on it such:
"Don't use IDs for styling". The reason is it will be a non re-usable rule.
and
"Don’t use adjoining classes". I couldn't properly figure out the reason without reading the css lint rules explanation and the reason is "While technically allowed in CSS, these aren't handled properly by Internet Explorer 6 and earlier".
I've something to tell you CSSLint. Nobody cares about IE 6 and earlier, we are in 2020, 99% of web software supports I.E. 11 as much, which was released on 2013 by the way and has less than a 3% usage.
By the other hand, where for God's sake it's expressed that all CSS rules have to be re-usable on the same view?
If you have a unique item on your web app, i.e. you likely have only one navbar, only one chat popup if any, only one navbar logo and only one footer for naming some, why do you need to add it's styling global? Isn't it such a bad practice that are contrary to all other languages around the world?
As an addition, let's say you have 5 views on your project. If you have a <nav id="primary-navigation">
it will be likely shown on all of them, so the same styling you put below #primary-navigation
is working on 5 different views. Isn't this a way of re-usability?
To increase the silliness around that both rules together, see how it lets you on a difficult situation.
If you can't use an id for styling <nav id="primary-navigation" class="dark-theme">
, and you can't add adjacent classes<nav class="primary-navigation dark-theme">
, how do you will manage to scope your styling and making it more maintainable, human readable and how do you manage it to add java-script event listeners when needed on an efficient way?
You simply can't.
What we could do semantically correct and easy to maintain? Let's set an example:
html: <nav id="primary-navigation" class="dark-theme"> Sass: #primary-navigation { // common styling for primary-navigation position: sticky; padding: 15px; // theming options &.dark-theme { background: #222; color: white; border-bottom: 1px solid #666; } &.light-theme { background: #fefefe; color: black; border-bottom: 1px solid #dedede; } }
Here's another statement that make me laugh for a while:
Avoid unqualified attribute selectors "Unqualified attribute selectors, such as [type=text], match all elements first and then check their attributes. This means unqualified attribute selectors have the same performance characteristics as the universal selector (*)"
My darling, this isn't different from what happen when using class, or id, or tag selector.
The stages of browser CSS rendering are:
- Building the DOM. The DOM is a tree-like data structure containing all of the HTML nodes on the page. Each node contains the data about that HTML element (such as attributes, ids, and classes) If the node has any HTML elements as children, it will also point to those child nodes.
- Building the CSSOM. When the browser encounters a CSS stylesheet (either embedded or external), it needs to parse the text into something it can use for style layouts and paints. The data structure that the browser turns CSS into is creatively named the CSSOM.
- Generating a Render Tree. In short, the render tree contains all of the information needed for the browser to create pixels on the page. The browser basically takes the DOM and CSSOM and squashes them together, removing anything that won’t have an effect on the rendered output.
- Layout and Painting. on Layout phase, the browser figures out the size and position of the elements (nothing rendered yet) and then, on the paint step it start generating pixels that we'll visualize.
As you can see you could get a little performance bias when nesting indiscriminately your selectors, like body>#main-content>.first-block .products-card p
as you will generate unnecessary child nodes into the CSSOM that will need to match the DOM and this will take a little more time than using simply a
.products-card p
, but as we will need to maintain this code and probably scale it, we must scope the rules so let's say #main-content .products-card p
.
It's a good practice but I've never saw a web app in which more specific selectors are causing load speed bottleneck. This usually is caused by iterating over and over again keeping tones of unused code that browser will add into CSSOM and will need to compare with DOM to figure out if it's OK to add it into the render tree or not.
For this and other many reasons is why you can find articles explaining each wrong point on CSSLint like this one which explain things on a more extensive way than I did here.
Summary
Linters are good for some things and bad for others.
They also can be edited to fit some project needs or some company or person preferences for avoiding these rules that are opinionated and adding other valuable rules that fit the project you're in.
There's many reasons for using linters on interpreted languages (on compiled ones, the IDE and the compiler will take care of this, but you can also add a Lint to check code formatting only or for getting warnings before compile to avoid waiting till compiler finds an error or ends to know if it's all OK).
There's few reasons for adding linters into HTML (only accessibility purposes may be useful I think) and there's fewer reasons for adding linters on CSS (that can be reduced to a code a formatting checker).
Have you used a Lint? If so, tell us in which language and context and what was your experience.
I personally liked it after few tweaks when using TypeScript.
As always, hope you like this post and best regards,
Joel