Guide 2017: Write Better JavaScript, Faster


Reading time ~18 minutes

A 4-part Crash Course in Composable JavaScript


Disclaimer: includes Dan Levy’s heretical musings thought-leadering.

If you want more complete examples, see my project escape-from-callback-mountain. Build Status

Part 1

Code Nirvana through 4 Key Rules

One of the most important ideas in software development is: keep it simple.

The funny thing is, every developer believes: they write simple & beautiful code, the real problem is those other developers.

Don’t worry, I’m not wading into the tarpit defining “Simple Code.”

Instead I’ll list 4 general rules, each which have helped me write more readable, testable & adaptable code.

  1. Restrict functions to single-purpose. See Single Responsibility Principle.
  2. Restrict functions to single-argument (or 2 when using NodeJS/Express callback (err, value) style). So, your 3 parameter function (a, b, c) => {} should instead accept ({a, b, c}) => {}. Helps with Dependency inversion, Interface segregation.
  3. Never use prototype if possible. (Confession: I only used once, by necessity, in escape-from-callback-mountain errors module.) Composition is a pure embrace of the Open/closed principle.
  4. Build functions without nesting/variable hoisting. Mitigate with ImmutableJS, else you can pass needed values as arguments. (Factory patterns are ok if state sharing is avoided or centralized. Examples in callback-mountain project.)

Let me emphasize: Single parameter functions are not restricting - simply use an Array or Object for your function argument.

Now, you may be thinking “hold on, is there a real difference between multiple arguments and wrapping everything in a single-arg object/array?” Great critical thinking (and thanks to Jani Hartikainen for bringing this up). The answer is yes, it’s a huge difference.

Essentially, parameter limiting can make your code compatible with many pre-existing patterns & tools: Promises, Array.[map,find,forEach,filter], RxJS, etc. This feels like an advantage to me.

If you are wondering how single-purpose functions ever amount to anything except code sprawl, well, let me introduce you to my friend, Higher Order Components, or HOCs. Which is really a fancy way of saying Function, Controller, Class, etc. The goal is to have your code feel like using LEGO™ building blocks.

Let’s look at some code…

Part 2

Four JavaScript Composition Examples

Let’s wire up a few functions to do some simple math.

Composable JS Example Includes:

  1. Pure ES6
  2. Lodash
  3. Array.reduce (built-ins)
  4. Promises
@author: Dan Levy <>
Twittering: @justsml
const test = require('tape')

// Demo of 4 techniques to 'glue' functions together (Higher Order Components)
// we'll do some simple math:  add5(5)==10,  half(10)==5,  square(5)==25.0

// Example/Util Math Methods:
// Pure-ish functions (
const add5 = n => {
  n = parseFloat(n) + 5  
  return n // required
const half = n => {
  n = (parseFloat(n) * 0.5).toFixed(2)
  return n
const square = n => {
  n = parseFloat(n * n).toFixed(2)
  return n

test('Method #1: Pure JS/ES6: math functions', t => {
  // `compose([functions])` accepts a list of functions, to be executed in order, starting with the value passed in
  const compose = (...fns) => x => fns.reduce((v, f) => f(v), x)

  const add5HalfSquare = compose(add5, half, square)
  t.equals(add5HalfSquare(5), '25.00', 'I can caz maths?')

test('Method #2: Lodash: math functions', t => {
  const {flow} = require('lodash');
  const add5HalfSquare = flow(add5, half, square);
  t.equals(add5HalfSquare(5), '25.00', 'I can caz maths?');

test('Method #3: Promises Chain: math functions', t => {
  const add5HalfSquare = n => Promise

  add5HalfSquare(5).then(answer => {
    t.equals(answer, '25.00', 'I can promise maths?');

test('Method #4: Array.reduce: math functions', t => {
  const add5HalfSquare = n => [add5, half, square]
    .reduce((val, fn) => fn && fn(val), n);
  t.equals(add5HalfSquare(5), '25.00', 'I can reduce maths?');

With this technique, you no longer have to fight the Framework Wars. I don’t have time for it.

Hip to Lodash?

Great, use _.flow, or .chain().

Using Promises?

Cool, Promises are just another pluggable pattern.

There are so many choices for gluing your functions together! Just make sure to insulate yourself by sticking to my 4 rules (in Part 1).

Let’s look at a more real-world example…

Part 3

Example Login Pattern

Let’s say we’re given requirements:

  1. User clicks [login] button.
  2. Modal opens, prompts for fields user and pass.
    • User submits form.
    • User clicks ‘forgot password’.
  3. Upon success, load app+msg data for logged-in user.
    • Cache ajax data for offline use.
    • Update user status to ‘online’
  4. Upon failure, plunge into fire pit.
  5. Show appropriate UI messaging

Using bluebird’s unique take on “Promises,” I’ll show the code in almost as many lines as the (terse) requirements above.

login = () => openLoginModal()
  .then({user, pass}) => ajaxLogin({user, pass}))
  .then(user => {
      setUserStatus('online') // < non blocking, as result is irrelevant.
      return [getContacts(user), getRooms(user), getMessages(user)]
  .then(([contacts, rooms, messages]) => {
      alertOnNew({contacts, messages})
      return this.cache({contacts, rooms, messages})
  .catch(UserCancel, hideModal)
  .catch(ForgotPassword, () => showUserMessage({message: 'think harder'}))
  .catch(LoginError, compose(hideModal, initFirePit, destroyApp))
  .catch(err => showUserMessage({message: 'Something truly unexpected happened, congratulations.'}))

That looks like some solid Functional JavaScript, however it still has some subtle weaknesses.

Namely - coupled code - see those anonymous arrow functions in .then(fn)? Are 2 distinct things happening here? This needs more attention, the call to setUserStatus doesn’t seem to relate to the returned new array structure. Let’s see if single responsibility can help us fix this up a little.

Let’s look at a refactored example…

Part 4

Summary with Further Optimized Code

Here’s how I applied rules #1 and #2 to refactor:

  • Avoid nested logic - it’s a sign you are mixing 2+ different things. It’s also a sign of untestable/high dimensionality.
  • More than 2 lines per function? Stop and think, or ask yourself/nearest dev: “Is it really related? Why is this together? Is there a better order to chain methods?”

Ok, I’m being a bit of a troll about an ‘absolute 2 line limit.’

Here is what I’m getting at: separate your code into distinct logical functions.

chatApp.getUserData = user => {
    return [getContacts(user), getRooms(user), getMessages(user)]
chatApp.login = () => openLoginModal()
    .then({user, pass}) => ajaxLogin({user, pass}))
    .tap(() => setUserStatus('online'))
    .tap(([contacts, rooms, messages]) => alertOnNew({contacts, messages}))
    .catch(UserCancel, hideModal)
    .catch(ForgotPassword, () => showUserMessage({message: 'think harder'}))
    .catch(LoginError, compose(hideModal, initFirePit, destroyApp))
    .catch(err => showUserMessage({message: 'Something truly unexpected happened, congratulations.'}))

Here’s why it’s better: it’s flatter & therefore more (unit) testable.

chatApp.getUserData is testable because it’s not hidden inside the login() and tied to the status update. Previously, the nested .then’s which called ad-hoc background functions are now explicitly not affecting the control flow of the chain (using tap). And as an added bonus, the decision tree of a function is easier to understand when using custom errors and filtering by Error type with Bluebirds’ .catch(<type>, <error>) interface. See my example pattern of a ‘Finite State Machine’ using the Error handling in Bluebird Promises

Ultimately my goal is code which reads like a story.

Alternative Theories

While I differ in approach & reasoning, I highly recommend reading Best practices for JavaScript function parameters on

If I forgot to give credit, please send a PR, or let me know

Functional Promises in JavaScript Crash Course

# Functional Promises in JavaScript Crash Course> See the companion project to this article: [`escape-from-callback-mountain`](https://gi...… Continue reading