5 habits that have improved my javascript

Enrique Alcántara
4 min readJul 28, 2015

--

Javascript is the programming language I’ve been using most of the time for the last 3 years. I use JavaScript in my job and personal projects. I write JavaScript for the web browser and for runtime environments like Node.

Even though I still have open questions about the best way to use the language, I’ve also developed habits that have improved the readability and maintainability of my code. These habits express my desire of choosing simple over complicated. They were born from many introspections over the structure of the code I’ve written in several projects.

Use a linter to verify the quality of your code

It’s hard to keep on with graph of concepts that exist in a large project. Make yourself a favor and use some tools to keep track of the quality of your code. A code linter is a must in any JavaScript project.

Keep functions small

The best way to handle complexity is by avoiding it. In the case of functions, the less responsibilities they have, the easier they are to understand and maintain. A turning point in the way that I implement functions happened when I started to adopt functional programming principles:

  1. A function has one, and only one responsibility.
  2. A function is referentially transparent. In other words, it doesn’t modify the state of anything outside its scope and always returns the same output given the same input.

Even though JavaScript has some characteristics of functional languages, it doesn’t enforce others like referencial transparency. It is harder to stick with those principles but it’s usually worth it. There are many resources in the Internet to learn about them.

Feel comfortable with map, filter and reduce

Another way to keep functions small is by relying on other functions (composition) that abstract common operations over lists. Manipulating lists is the bread and butter of any computer program hence having a simple idiom to express those operations is useful. Javascript’s Array provides important functions like filter, map and reduce that make list manipulation clearer.

There are two ways of working with lists in JavaScript: The imperative, C style way, that makes really explicit how you iterate and manipulate the list and the functional, declarative way, that uses verbs to express what operations you want to perform:

//Imperative style
function expiredProductsTotal(products) {
let total = 0
for(let product of products) {
if(product.status === 'expired') {
total += product.price
}
}
return total
}
//Functional style
function expiredProductsTotal(products) {
return products
.filter(p => p.status === 'expired')
.map(p => p.price)
.reduce((total, products) => t + p, 0)
}

Reusability increases as you find that many of these operations are common across your system. They become concepts of their own and can be exported in modules:

//Functional style
function expiredProductsTotal(products) {
return sum(pick(expired(products), 'price'))
}
function pick(xs, property) {
return xs.map(x => x[property] || null)
}
function sum(xs) {
return xs.reduce((x,y) => x + y)
}
function expired(products) {
return products.filter(p => p.status === 'expired')
}

Embrace modularity

The best way of taking advantage of small functions is by putting them into modules. Modules are the backbone of reusability in software development. Depending on the environment you execute JavaScript, creating modules can be less or more complicated.

I won’t focus on technical details of how to create modules but on the characteristics of a good one. A good module:

  1. Is cohesive: It provides functionality that is conceptually related. Having functions to make HTTP requests and connecting to a DB in the same module is a bad example of this. Sometimes the line between cohesive and disconnected is blurrier. Having small modules makes the distinction clearer.
  2. Is context independent: It doesn’t make many assumptions about the environment like the existence of global variables. If the module is intended to be a multi platform library, it should not be coupled to specific way in which an operation system handle things like networking or the file system.
  3. Doesn’t expose unnecessary details: By hiding complexity and providing a well defined, predictable interface.

If we have modules that already provide some of the operations used in the previous example, the code gets much smaller.

//Using CommonJS syntax 
let { pick, sum } = require('collection-utils')
let { expired } = require('product-filters')
function expiredProductsTotal(products) {
return sum(pick(expired(products), 'price'))
}

Remember, the first step to create good modules is creating small functions.

Fall in love with unit tests

Writing unit tests is the habit that took me most time to adopt. More specifically, writing them first entailed a long learning curve. What this habit brought to my craft was the ability to declare the intentions of my code before writing it. This is a powerful shift in the mindset of any developer and leads to:

  1. Clarity: You are forced to think about what before how. Going straight to the implementation, mixes both concerns and leads to technical details of the implementation to influence the intention of the code. You should always try to keep this relationship working in the opposite direction.
  2. A well defined scope: When you write a test stating that a function F returns Y given input X and you limit its implementation to satisfy that condition, the scope of that function can’t grow outside the test’s boundaries.
  3. Explicit dependencies: A unit test is just another context in which your code runs. You should prepare the environment (open a db connection, passing settings, importing other modules, etc) in the same way that you would do it when running in production. That usually makes dependencies explicit. Too many dependencies are a sign complexity.

You may not get all these benefits immediately. If you’ve never attempted to adopt this practice, it can feel as overwhelming as adopting a new paradigm. With tenacity and lot of practice you’ll start feeling more engaged and over the time it’ll be weird not to start with a test.

All these habits are tightly related. They complement each other and developing one of them usually leads to acquire benefits of another. I still see myself breaking them from time to time but I try really hard not to. Being a good developer, after all, is about creating good habits.

--

--

Enrique Alcántara

Software developer consultant. I live and breathe software design.