Writing resilient, unbreakable code using functional patterns

Quality of an abstraction depends on the quality of its units

Rajesh Naroth
Jun 23, 2020 · 5 min read
Image for post
Image for post
Image by Edi Nugraha from Pixabay

Fresh grads, recruits from coding camps, ex-Java/Perl/Ruby/Python coders tend to write in an imperative style. There is nothing wrong with it, I was in the same boat. I remember sometimes certain data transformation functions were so nested with conditional logic strewn through out, I couldn’t reason my own code after a few months. Debugging was a nightmare. Then I read up on functional programming and it changed the way I wrote code.

An example

We’re going to write a simple function that takes in an array of numbers and return anything less than 7 as a new array of formatted strings. If your function looks something like this, you are not alone.

It works. What is the issue then?

It is imperative. You are telling the computer explicit instructions to do this, add this, do that and so on. Imperative code is rife with potential error points. and it relies on state mutation. earlyHours() function needs two temporary state values to do its computation., both are mutated many times. “i” is initialized and incremented in every loop. “early” is also initialized and built out in increments. Check the image below. Note the parts annotated by explosions and underlines.

Image for post
Image for post

There are about seven potential areas for bugs here.

Pure functions

First step in using a functional style is to learn to write pure functions. A pure function has the following properties:

  • They are 100% predictable. For a given a set of input arguments, the return value is always the same.
  • It will not access or mutate anything outside its scope.
  • It will not mutate its function arguments.
  • Return values are always fresh copies of data. This means that you cannot return an object that still refers to properties inside the input arguments

Using pure functions as the basis of your design brings an important quality to your design: immutability. This means that mutations will occur only where explicitly specified, there will not be any accidental ones.

Functional style

When it comes to data transformations of lists, there are three methods we must master. map, filter and reduce. Our function can be rewritten as follows:

There are no temporary state variables here and only two potential points for bugs, one in the comparison and the other in the template string.

Functional tools such as map and filter are time tested, iteration is handled internally. Also remember, forEach is still imperative because to be effective, it will need to mutate state outside its scope.

Code Surface area

For fun, I took screenshots of both functions on codepen and measured how much space they took.

Using a functional style, you are able to reduce the code surface to 23% of the original. The less code surface you have, the lower chances of bugs.

Going further in improving code quality

But is it enough to just adopt better patterns? What else can you make your function unbreakable?

Add input validations

What if the input to the function is invalid? The function will crash. In this example, you can use JavaScript’s optional chaining to protect from bad inputs.

Add default values to function arguments

If applicable, you should use default values for function arguments. This can remove unnecessary null/undefined checks most of the time.

Return meaningful empty values

I recommend returning meaningful empty values such as ``, {} or [] instead of null and undefined. This means that the caller of the function can safely use string and array functions without having to worry about undefined error.

Unit tests

Hopefully your projects are strapped with a test framework. You can unit test pure functions very easily.

You can add tests for several more scenarios like these.

Static Typing

Static typing with TypeScript can prevent your function from being invoked improperly.

This makes it impossible to call the function with anything other than an array of numbers. TypeScript is not present during runtime thus will not protect you from type incompatibility.

You can play with the CodeSandbox here: https://codepen.io/rajeshnaroth/pen/WNrjybX

Use a functional utility library

Beyond map, reduce and filter there are several utilities that can help you with a functional style of programming. lodash and ramda are two very popular libraries. I like ramda for its API design. As homework, I recommend you look into functions such as compose, pipe, flatten, pick, find etc.

Imperative code is essential, otherwise you will just have a static site. Your application will need to go from one state to another based on user/browser/network events. What is great about JavaScript is that it supports both styles.

Summary

  1. Write pure functions
  2. Use functional patterns such as map/reduce/filter/compose
  3. Add input validations.
  4. Use defaults in arguments
  5. Return empty objects instead of null or undefined
  6. Write unit tests
  7. Use static typing

To build high quality abstractions, build them with resilient, compose-able units. These tips can help you achieve that.

JavaScript In Plain English

New JavaScript + Web Development articles every day.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store