Oops — Craig Sunter (CC BY-ND 2.0)

The Single Biggest Mistake Programmers Make Every Day

Eric Elliott
Nov 5, 2015 · 14 min read
  • Use pure functions wherever you can. A pure function does not mutate anything outside itself. A pure function does not produce side effects. Given the same inputs, a pure function will always return the same output.
  • Notice how much of the program state can be represented as lists of things.

Keep it Stupid Simple

There is a common design principle that (according to Wikipedia) originated in the US Navy:

What is Simple Code?

Simple, from the latin simplus, originally referred to a medicine made from one constituent, especially from one plant. The meaning I refer to here is “not complex or compound; single.” Simple vs complex can be summed up concisely: one thing vs many things.

  • Simple to use.
  • Simple to reuse.
  • Simple to extend.

The Process

My first time around the room, I noticed that students were getting stuck in rabbit holes. One group was stuck creating a build process. Another was stuck working out all the data structures. Another was trying to work out how to apply a functional data flow to the problem before there were any functions for data to flow through.

  1. Make it right.
  2. Make it fast.

What does “make it work” mean?

Since I love immediate feedback, I always start with a few simple unit tests that will alert me immediately when I have a solution that works. During the unit tests, I decide what I mean by “make it work”. What are the minimum requirements that the solution needs to satisfy?

  • What are you doing today?
  • Is there anything blocking you?
`should display a text input field for the user's name.`
`should display a "Sign In" button.`
`should save current logged-in user in client state.`
  • Begin at the beginning. If the task is to identify what the current user is doing, the first step is probably to identify who the current user is.
  • Do one thing at a time. Don’t try to squeeze all the app requirements into the first module you write. Chances are your finished app will consist of many modules. Keep each module small and focused, and concentrate on one module at a time.
  • Start small and iterate. Write a test. Make it pass. Refactor. Make it work. Make it right. Make it fast. One step at a time. Keep taking those steps until the job is done.

Simple to Understand

Do one thing per line.

Avoid Side Effects

The essence of any program is to take some data as input and produce some data as output.

foo(...inputs) => output
const result = foo(a, b, c);

“Be liberal in what you accept, and conservative in what you send.”

My interpretation is a bit more general:

“Expect the worst. Behave your best.”

In short, if any change occurs that is not communicated by the function signature, it’s a side-effect.

Favor Consistency

If you call the same function several times with the same input, and it returns different outputs, it’s not always obvious why that is. Some functions rely on random input, or differ in output depending upon the time it was called, or the value of some outside variable that isn’t specified in the function signature.

const foo = f(a);
const foo = 42;

Pure Functions are the Simplest Functions

Functions which have no side-effects and exhibit referential transparency are called pure functions.

  • Produces no side effects.

Simple to Use

Simple to use means that the code should have a clear and focused API. The code does one thing, and it has a simple interface to accomplish that thing.

Avoid Ad-Hoc Polymorphism

It’s possible in JavaScript to use ad-hoc polymorphism to create a single function that does many different things. For example, jQuery accepts many different types of inputs to the jQuery function.

import { pageReady, $ } from 'jQuery';pageReady(() => {
$('.your-selector').on('click', () => {
doStuff();
});
});

Encapsulate Private Data

I believe that the object you export from a module should also serve as documentation for that module. In other words, it should expose the supported interface, and only the supported interface.

const foo = () => {
return {
_secret: ‘yeah, nobody could possibly find this…’,
getSecret () {
return this._secret;
}
};
};
const bar = foo();console.log(bar._secret); // yeah, nobody could possibly find this…
  • Experienced developers think it doesn’t apply to them. “I know what I’m doing. This warning is just for newbies.” So they ignore it, too. Admit it, experienced devs. You’ve done this before. So have I. Apologies to the three of you who have better discipline than the rest of us. ;)
const counter = function counter() {
let count = 0;
return {
getCount() {
return count;
},
increment() {
count += 1;
return this;
}
};
};
const myCounter = counter();
console.log(typeof myCounter.count); // undefined -- private!
myCounter.increment().increment().increment();
console.log(myCounter.getCount()); // 3

Pure function > Function > Factory > Class

Notice that these things increase in complexity as you move from left to right. Start on the left side and move to the right only as needed.

Simple to Reuse

Simple to reuse means that you should be able to extract your code into its own module, import it somewhere else, and use it without breaking a bunch of things or committing to a time consuming refactor. Ideally, this should be one cut, one paste, and a couple `import` statements.

Simple to Extend

For stability, API’s should be closed to breaking changes, but open to extension. For functions, this could mean passing named options into a function rather than a list of arguments so that you avoid passing a bunch of values that may or may not be semantically named at the function call site.

// Individual arguments
const item = createFilmItem(title, null, null, recommendedPrice);
// Options object
const item = createFilmItem({ title, recommendedPrice });

Export Factories Instead of Classes

As I’ve mentioned many times before, `class` affords `extends` like balls afford throwing and chairs afford sitting, but `extends` leads unwary developers down the path to the fragile base class problem, the gorilla banana problem, etc…

Conclusion

Simplify your code. Start with the least complicated implementation and work your way toward more complex solutions only when the problem demands it.

Remember:

  • Keep It Stupid Simple (KISS)
  • Make it work, make it right, make it fast.
  • Understand the problem. (Know what “make it work” means.)
  • Begin at the beginning.
  • Start with tests.
  • Do One Thing (DOT).
  • Start small and iterate.
  • Pure function > Function > Factory > Class

Learn JavaScript with Eric Elliott

  • Online courses + regular webcasts
  • Software testing
  • The Two Pillars of JavaScript (prototypal OO + functional programming)
  • Universal JavaScript
  • Node
  • React

JavaScript Scene

JavaScript, software leadership, software development, and related technologies.

Thanks to JS_Cheerleader.

Eric Elliott

Written by

Make some magic. #JavaScript

JavaScript Scene

JavaScript, software leadership, software development, and related technologies.