I’ve already spoken about my initial ESLint explorations (Part 1) and contributions (Part 2). For Part 3, let’s get a little deeper. Why use ESLint in the first place? Why is it so hard for a group of people to agree on one style guide? Can we at least make it easier to compare configurations?
When I was on my Walmart Labs contract in 2014, I was working with the people behind Hapi and its collection of associated modules. They had their own approach to formatting Javascript. In some cases it matched up with what I had seen before in the wild, and in others their approach was totally unique. A few examples:
require()
or import
must always be uppercasefunction
and ()
function()
declarationThey knew they had a strange style, so they put some good time into assembling a comprehensive style guide. I took a look, was quickly overwhelmed, then went back to the code. The existing code would be a good enough guide, right? And it was simple enough to tell my editor to switch to four space indentation.
But I struggled with the new format. If asked to say what the rules were I could tell you, but muscle memory took over when I wrote code. I kept leaving out that empty first line of every function. And I had to resist my instinct that uppercase variables were always a class that I would use with new
. I knew I was having difficulty, so I checked before commit with GitX, then re-checked the whole pull request diff with Github Enterprise before submit.
When I did submit my pull requests, even after all that, I got a lot of feedback about style. Lots of missing empty lines, missing spaces between function
and ()
. I got better over time, but just about all of my pull requests had some sort of style feedback. I addressed the feedback quickly, but it slowed down my approvals.
I believe that the style distraction reduced the amount of review time spent on the actual problem domain, the features I had implemented.
What’s wrong with this picture?
I strongly believe that something as simple as code formatting should never be discussed in a pull request. That’s not the time for it! The official project style should be discussed and ratified at the beginning of the project, then tools put in place to enforce it. Then the team has more time to focus on their business impact!
That’s why I like ESLint. I was planning to release an open-source project recently, and I dug up my old release checklist from when I released eight projects in 2014. It had some file/project/variable naming guidance on it, and I realized that I could simplify if I found a plugin to check it for me. That’s how I discovered eslint-plugin-filenames
. I installed the right ’snake case’ regular expression, and I had one less manual check!
The sky’s the limit for additional checks! There are lots of plugins out there, or you could write your own. The point is that these kinds of things can make a team substantially more efficient. Instant feedback in the editor instead of diff comments.
Even with the tools, the challenge is getting to that initial agreement. I have to say, I was surprised when that Silicon Valley clip about Tabs vs. Spaces made the rounds recently. Truly, I thought the issue already had been decided in favor of spaces. This survey drove home that it really isn’t.
How was I so mistaken? Maybe it’s because I’m pretty deep in the Javascript community? Or I’d been burned too many times with horrible creative tab width rendering and lack of configurability?
I was similarly surprised when I recently discovered some big differences between me and my friends. For example, should braces be formatted like this:
if (something) {
doSomething();
}
else {
doElse();
}
Or like this?
if (something) {
doSomething();
} else {
doElse();
}
Based on my conversations it was about evenly split, perhaps leaning a little more towards the second brace style. You can tell how divisive this is just by looking at the names for these styles in the ESLint documentation: the first is called Stroustrup and the second is called the one true brace style. I sense some bias. At least there’s an option. :0)
You can see other, more Javascript-specific arguments happening all the time. Should your code include semicolons? Indent two or four spaces? Multiple var statements or just one?
How can there be so much disagreement in one language? I can see how there might be big differences between different languages, but within the same community? And I know there’s absolutely no objectivity with these kinds of things. Neither of those those brace formats is objectively easier to read.
But it might be easier to read for an individual.
I’ve come to believe that it all has to do with an individual’s unique history. You came from somewhere before Javascript, and then when you started with Javascript, you read code written by people who had come from somewhere. You are colored by all of these experiences. Your code quality intuitions and ability to absorb the meaning behind code quickly has been trained by all the code you’ve seen in the past.
I acknowledge that my preference for function()
over function ()
probably comes from my history of language use. From the beginning:
That’s my fingerprint. Everything I see is filtered through all of my previous experiences. You can see why I feel right at home with curly braces. I often appeal to objective readability when discussing my dislike of Ruby’s paren-less function calls, but it has more to do with my history.
What’s your fingerprint? What if we could quantify it?
Imagine that you’re about to start a project. Or you’re about to join a new company or contract. You were using ESLint before, with all your own preferences, and the people you’ll soon be working with have their own configuration. You both pull in different plugins, and started from a different comprehensive configuration like AirBnB or “Standard”.
Maybe the resultant behavior is similar, or maybe not! It’s very hard to tell! How do you start to talk about the differences?
Introducing @scottnonnenberg/eslint-compare-config
! It’s a simple node module with a CLI and an API. You can install it and see the differences between two projects’ ESLint configurations:
npm install -g @scottnonnenberg/eslint-compare-config
eslint-compare-config yourProjectDir/ theirProjectDir/
This is not a simple load and diff of each project’s .eslintrc.js
. It takes into account all of ESLint’s sophisticated configuration behaviors:
eslint-plugin-NAME
node module, which means that Node.js dependency lookup rules come into play.Why rewrite all that? I just tell ESLint to load the configuration for me! I put a configuration-loading Node.js script in the target directory, run it, then delete it afterwards:
try {
var result = childProcess.execFileSync('node', [target], options).toString();
return JSON.parse(result);
}
finally {
fs.unlinkSync(target);
}
Here’s what it looks like in a minimal scenario. I’ve added code to my blog-code
project for this post, in the eslint-comparison/
directory:
git clone git@github.com:scottnonnenberg/blog-code.git
cd blog-code/eslint-comparison/thehelp
npm install
eslint-compare-config . one-rule/
And that produces:
Plugins shared: 8
filenames
import
security
@scottnonnenberg/thehelp
immutable
no-loops
jsx-a11y
react
Plugins missing from left: None
Plugins missing from right: None
Extends shared: 3
@scottnonnenberg/thehelp
@scottnonnenberg/thehelp/react
@scottnonnenberg/thehelp/functional
Extends missing from left: None
Extends missing from right: None
Rules matching: 278
[full list omitted for brevity]
Rules missing from left: None
Rules missing from right: None
Rule configuration differences: 1
max-nested-callbacks:
left: [ 'error', { max: 3 } ]
right: [ 'error', { max: 4 } ]
Differences in other configuration: None
I just released my own configuration. How does it compare with some of the big players? I just happen to have included a similarity score in my tool to boil all the differences down into one number. It takes rule settings into account, nothing else.
I pulled down a number of the most well-known configurations, then used my tool to generate the matrix of similarity scores:
"Standard" | AirBnB | defaults | Walmart | indent | ||
thehelp | 28% | 42% | 38% | 10% | 38% | 72% |
indent | 29% | 41% | 43% | 10% | 38% | ↵ |
Walmart | 39% | 39% | 39% | 21% | ↵ | |
defaults | 22% | 14% | 17% | ↵ | ||
52% | 40% | ↵ | ||||
AirBnB | 39% | ↵ |
A few notes:
--score
option. Go grab the project with everything set up for this!It’s no surprise that thehelp
and indent
are similar - my friend Jamie Hoover is behind indent
, and we’ve had some good conversations going over our rules in detail. Facilitated by my comparison tool, of course! :0)
Use @scottnonnenberg/eslint-compare-config
to compare configurations, quantify differences, even stimulate conversation! You can also use its getConfigSync()
API method or ESLint config load script directly to figure out exactly what all of your extends
finally resolve to.
Just remember to put coding style rules in place when starting a new project, and use the tools at your disposal to automate it all. Then move on and be done with it! You’ll be able to better focus on the customer!
It may feel satisfying to use punishment to get people to do what you want. But by doing that you ignore human psychology, creating resentment which will eventually make it harder to achieve your... Read more »
I recently wrote about my ESLint exploration and configuration node module. But I went further than that - I contributed back to the ESLint community! I submitted several pull requests and released... Read more »