« Scott Nonnenberg


ESLint Part 3: Analysis

ESLint Logo

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?

Trying to adapt

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:

They 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?

The importance of tools

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.

Preferences and controversy

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.

Your fingerprint

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:

  1. Pascal
  2. A little bit of Javascript
  3. VBScript
  4. C++
  5. Java
  6. A little bit of Javascript
  7. C# and a bit of Visual Basic .NET
  8. Ruby and a little bit of Javascript
  9. Objective C
  10. CoffeeScript
  11. Lots of Javascript

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?

Announcing: My comparison tool!

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:

  1. Lookup scheme for loading plugins - given a plugin name, ESLint attempts to load the eslint-plugin-NAME node module, which means that Node.js dependency lookup rules come into play.
  2. Extending one configuration from another - a final configuration can be built from many recursive loads and merges. Take look at all the merging ESLint has to do to load a Walmart ESLint config!
  3. Hierarchical merging - To figure out the ESLint configuration for a given file, ESLint will look up the directory hierarchy for parent configuration files, then merge it all together.

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

A little bit of fun

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 Google 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%
Google 52% 40%
AirBnB 39%

A few notes:

  • Where possible, I chose the ES6/React configuration available with the system. “Standard”, Defaults, Google did not have the option.
  • Default and Walmart both pursue minimalism in their ruleset: turning rules on with default configuration, and leaving quite a few rules off. This results in lower, more consistent scores.
  • You can see the details behind these numbers by omitting the --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 the tools!

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!

I won't share your email with anyone. See previous emails.

It's me!
Hi, I'm Scott. I've written both server and client code in many languages for many employers and clients. I've also got a bit of an unusual perspective, since I've spent time in roles outside the pure 'software developer.'

NEXT:

Carrots, not sticks 2016 Jun 23

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 »

PREVIOUS:

ESLint Part 2: Contribution 2016 Jun 16

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 a... Read more »