In this article, I will talk specifically about Javascript but most of these principals can be applied to any language.


Typically when we talk about immutability in other languages we consider problems such as race conditions, management of shared mutable data and thread-safety. While we don't think about thread safety as much in Javascript some significant benefits come with writing immutable code.

Mutability hides change which can end up creating unexpected side-effects. Keeping things mutable simplifies your applications mental model and makes it easier to reason about.

Numbers and strings are already immutable in Javascript. This is why we can't do:

string = "string"; string\[0\] = "x"; string // string

But objects and arrays, on the other hand, can be mutated. For example:

const person1 = { fname: "Jake", lname: "Ford" };  
const person2 = person1;  
person2.fname = "Mike";

When we read this code it looks like person ones name if Jake and person twos name is Mike. Both their names are Mike. Not quite what we were after. Instead, if we did this.

const person1 = { fname: "Jake", lname: "Ford" };  
const person2 = Object.assign({}, person1);  
person2.fname = "Mike";

Object assign creates a copy rather than a reference and with it the behaviour you would expect if you landed on earth with no knowledge of Javascript and read this code top to bottom it does what you expect.

Surely this creates a tonne of memory overhead because we keep copying data? Enter structural sharing. It's some fancy stuff that the interpreter does under the hood to optimise for immutable behaviour like this.


Much like keeping things immutable makes your code easy to read, follow and reason with. Not reassigning variables also fit into this bracket. Take the following example.

let type;  
let input = "mortgages";  

if(input === "savings) {  
 type === "banking";  
} else if(input === "home") {  
 type === "property";  
} else if(input === "mortgages") {  
 type === "mortgage";  
} else {  
 type === "not-found";  

Instead of reassigning the value, we could do:

const type = \["savings", "home", "mortgages"\].reduce((acc, value) => {  
 input === value ? value : acc;  
}, 'not-found')

Any time I look at this block of code I know that type is going to be a result of the reduce function. It's not going to change anywhere down the line. It is easier to reason with than a `let` or `var` declaration that could change at any point in the scope unless we read the entire block.


Finally (this tip I have to credit to fellow programmer Callum who opened my eyes to the benefits of self-documenting code). By this point, we can all probably agree that unless it's `i` naming variables as single letters make the code harder to follow. But the next step to this is naming variables so they document themselves. Take the following example:

let a = 100000;  
let b = 0.1;  
let c = a \* c;

at this point, we would be guessing.

let amount = 100000;   
let interest = 0.1;
let total = amount \* interest;

now we are starting to see more of the picture.

let mortgageAmount = 100000;
let interestRate = 0.1;
let mortgageTotalToRepay = mortgageAmount \* interestRate;

not we have the full picture. I don't need to read and evaluate the code to figure out what these variables are.

All of the above examples are trivial and the tips are very simple. However, in my day job, I often see code that could be more readable and therefore easier to maintain if these 3 tips were followed.

Thanks for reading!