The JavaScript that I like

April 18, 2018 javascript

I like JavaScript a lot! And I write a lot of JavaScript code lately - Node.js backends, some AWS Lambda functions, and the occasional frontend. (I still regularly write Go, Ruby, and Elixir, too.)

I know my opinion is contrary to the popular one. JavaScript has a stigma of a “bad language,” a language with no grace and no sense to it, a language for dummies.

I’m not going to criticize or evangelize JavaScript here. Because, as I came to realize, there are many ways to use JavaScript. And “my” JavaScript is full of assumptions and conventions that are essential to my productivity and enjoyment.

Let’s unwrap these assumptions one at a time.

My JS uses Webpack

I use Webpack to create builds, even if the target platform is Node.js.

For me, Webpack is like a compiler. It ensures that the syntax of all files is correct, that all imports are present. If I want, it creates a package that has no dependencies (useful for AWS Lambda functions). It supports compiler constants and conditionals. It optimizes code and provides analytic features to cut down bundle size.

But most of all, I like Webpack because it’s consistent across all platforms that I use, and I don’t have to worry about minutiae.

My JS uses modern JavaScript syntax

Babel is a game-changing tool. It lets us write against a standard that’s not constrained by what the browsers support - a major pain point of pre-Babel JavaScript.

Thanks to Babel, we can have arrow functions, object splats, string interpolation, and even JSX, and still support any ancient browser.

Sidenote: Of all the uncommon syntactic sugar, I only advocate JSX.

My JS is rigidly linted with ESLint

JavaScript is known to be too permissive, to the point of being a joke. For just one common example, you can assign to global variables without declaring them:

function hello(name)
  result = `Hello, ${name}`
  return result;
}
hello('World');
console.log(result); // oops

Yes, the permissiveness is a big problem, demanding vigilance and attention from the programmer.

But fortunately, ESLint will warn you of such issues. That’s why I don’t even think of writing JavaScript without a linter.

Using ESLint renders the permissiveness argument moot. It helps with many other common issues as well.

My JS is formatted with Prettier

Prettier is still a novelty. It’s a “code formatter.” When I save a JS file, Prettier will update it to be, well, “pretty”: from

function foo() console.log('bar')

foo(long,list,_of,arguments, i: dont, worry: about, properly: indenting, 'them')

to:

function foo() {
  console.log("bar");
}

foo(
  long,
  list,
  _of,
  arguments,
  {
    i: dont,
    worry: about,
    properly: indenting
  },
  "them"
);

` I only need to care for my code to be correct, and Prettier takes care of the rest: it inserts line breaks, indents nested function calls, aligns object declarations, adds and removes brackets, and so on. It’s so much easier to write code when I don’t care about formatting (and it still comes out beautiful!)

Where Prettier shines is refactoring, when I move lots of code around. No more boring repetitive re-formatting and cleanup.

As a second (but not secondary!) benefit, Prettier enforces a consistent code style. You can’t mess it up even if you really want to. We don’t get into PR arguments about code style. Also, it’s relatively painless to prettify existing code; we already migrated several large projects to Prettier.

My JS uses ES6 modules

import doStuff from "./doStuff";

The ES6 import/export syntax is expressive. It stands out in a file, unlike require() calls. It offers syntax for partial imports and renames.

It’s clear by now that ES6 modules are going to be adopted by browsers, and eventually by Node.js, but the adoption question is mostly moot because I use Webpack.

Worse things than ES6 modules: AMD; horrible weird Angular.JS dependency management.

My JS does not use classes

JavaScript was born with very robust functional programming features. Unfortunately, we live in the age of Object Oriented Programming, and of course, we need Classes and Inheritance and Design Patterns in your JavaScript code. Well, count me out.

Classes are a harbinger of concealed state. I avoid them like the plague.

This applies to good old prototype-based JavaScript OOP, as well. But classes are worse because they are more accessible to part-time JavaScript dilettantes.

Sidenote: To be fair, classes do have a place in a JS program, and that’s on the very bottom of the abstraction stack, where they encapsulate some genuinely isolated state. 

My JS uses modules for encapsulation.

The most common reason to use classes is not for inheritance or polymorphism. It’s to encapsulate logic.

Well, I do the same thing with modules: I export the interface and conceal the implementation details. You can even have real private with modules.

One place where I regularly see people use classes instead of modules is React. React components should be functions! And if you need to split up a component, good for you - place the helper function in the same module.

function authorTitle(author)
  // ...
}

export default function Comment(props...)
  // ... uses authorTitle
}

Perhaps I need to bring attention to the fact that modules are scopes, and they don’t leak definitions all around the global namespace - another benefit to using Webpack.

My JS has one function export per module

In application-level business logic (not in a library), one export per module will ensure a comprehensible module length, tight scoping, and a slim and easy to navigate dependency graph.

It’s much easier to understand 10 10-line modules than one 100-line module.

My JS is tested with Mocha

Every module has to have its own test file. I prefer writing tests with Mocha (and Chai and Sinon, situationally).

Unit tests are an integral part of modern software engineering, especially in a dynamically typed language like JavaScript. So I won’t elaborate on why it’s a good thing.

Still, some people dismiss writing unit tests for JavaScript because it’s “just the frontend” or “not serious code.” They are wrong. Write your tests.

My JS uses promises for asynchronous flow

Promises are supported by all modern platforms. I don’t use third-party implementations like Bluebird, and I don’t use fancier technologies like channels or reactive programming.

Promises, for me, are the sweet spot for JavaScript: well adopted, easy to understand, flexible.

Many APIs that use callbacks also offer promise syntax. (for example, the AWS SDK). Other APIs are easy to wrap in a promise.

One often ignored feature of promises is concurrent flow with Promise.all() - check it out.

My JS uses Yarn

Yarn takes most of the pain out of package management. Concretely, it works fast, ensures a consistent set of package versions, and the lock file produces clear diffs.

Yarn has issues, but no dealbreakers.

My JS uses simplistic build scripts

A build system with gulp or grunt is notoriously complex. So I never put these into a project.

I start off declaring build commands in the scripts section of package.json. If something is more complicated - like there are dependencies, or I need environment variables - I use Makefiles (yes, GNU make, the granddaddy of build systems. It’s as simple as it gets.). I also write maintenance task as simple JavaScript files.

And if there is something related to processing the source code, I integrate that into Webpack. Since it can watch for changes and provide a processing pipeline, Webpack obviates gulp/grunt.

My JS is continuously tested

Any test is useless if no one runs it. The best weapon against carelessness - yours or your coworkers’ - is CI. Again, since it’s “just JavaScript,” people sometimes neglect to continuously integrate it. They are wrong.

We use CircleCI, and I love their Docker-based build configuration system.

In conclusion

So there you have it. These assumptions aren’t dogma, and they aren’t even tips or suggestions that everyone should follow. But if you have problems with JavaScript, it could be that you’re doing one of these wrong.

Don’t blame the language - be the driver of change and enjoyment and productivity will quickly follow.

Buy Me a Coffee at ko-fi.com