What’s wrong with mutability or mutating the objects in code?
Doesn’t it make things simple?
Before we go in-depth of understanding the importance of immutability we should first understand the concept of mutability and immutability in software development.
A princess kisses a frog hoping it will turn into a handsome prince. The concept of immutability says that a frog will always be a frog. The immutability concept is mainly originated from functional and object-oriented programming. Whenever we want to make changes to some data (for example to an object or an array) we should get a new object back with the updated data instead of directly modifying the original one.
Think of immutability as “save as” because you know it returns a newly changed object while traditional in-place mutation would be like “save” means updating the original and letting go of an earlier state. The structure of the immutable data cannot be changed.
The data would be the original one and once the object is created we can not modify its state in an immutable object. Consider the immutability concept as the human body which is not affected by the outside world. A state with peace of mind.
The concept of mutation is vice versa. Mutability describes whether the state of an object can be modified after it’s declaration or not. Consider it as kissing frog results in the transformation of a prince. Take an example that we have a variable, and we assign a value to it. Later if we need to modify the value of this variable, and we change it then changing its state, the object is considered to be mutable.
const a = [2, 1, 4, 3]; console.log(a); // output: [2, 1, 4, 3] const b= a.sort(); console.log(b); // output: [1, 2, 3, 4] console.log(a) // output: [1, 2, 3, 4]
We have assigned an array to variable “a” and we are performing the sorting operation on it. The sorted array is assigned to variable “b” and you can see that we are getting the sorted array for both the variables after performing the sorting operation. Here objects and arrays are passed as a reference instead of copying over both variables “a” and “b” point to the same array in memory and that array was directly mutated by the method sort.
Let’s understand this concept with one more example. We have an array of data that contains the number of days one employee worked on some project.
var daysWorked = [7, 7, 5, 9, 8, 6, 6, 7, 9, 5]; console.log(daysWorked); // op: [7, 7, 5, 9, 8, 6, 6, 7, 9, 5]; let's check last 6 months of work var lastSixMonths = daysWorked.slice(0,6); console.log(lastSixMonths); // op: [7, 7, 5, 9, 8, 6] // let's sort the days worked var sortedDaysWorked = daysWorked.sort(); console.log(sortedDaysWorked); // op: [5, 5, 6, 6, 7, 7, 7, 8, 9, 9] var lastSevenMonths = hoursWorked.slice(0,7); console.log(lastSevenMonths); // op: [5, 5, 6, 6, 7, 7] but the output was [7, 7, 5, 9, 8, 6] expected.
In the above method, we have used two methods on variable daysWorked.
- Slice: In the above example we are achieving immutability using the slice method. This method slices the original object and instead of modifying the original dataset, we are getting the new copy of the object lastSixMonths.
- Sort: This method is a mutable way and it is sorting the original dataset. This method is preventing us to make further calculations on the dataset.
What is the good part of the immutability? Why do we need to care about the immutability in the first place? Why even bother?
Immutability gives stricter control over your data immediately making your code safer and more predictable. In other words, immutable objects allow you to control the interface and data flow in a predictable manner, discovering the changes efficiently. It also makes it easier to implement complex features such as undo/redo, time travel debugging, optimistic updates and rollback, and so on.
If we talk about the frontend library React, Vue os state management library Redux then immutability can also help achieve better performance by enabling quick and cheap comparisons between versions of the state before and after changes. Components can take advantage of this and intelligently re-render itself only when needed. This can significantly boost performance.
The main benefits of immutability are predictability, performance, and better mutation tracking.
In any application when we work on some front-end libraries, we declare a lot of state in it. We perform the asynchronous action and it updates the original state (mutation). Once the end-user starts using it the updated state will be significantly different from the initial state. Mutating the state hides changes and it creates side effects that can cause several bugs. Debugging becomes hard in such cases.
When you keep your application architecture immutable and mental model simple it becomes easier to predict the data in the state at any given time and then you can rest assured that it won’t create any nasty side effects.
Creating an immutable object cost memory. How? When you add value to an immutable object you need to create a new object and in this new object, you copy the existing value with the new value which requires additional memory. To reduce memory consumption we use structural sharing.
Whatever update we make it returns new values, but we share the structures internally to reduce memory consumption. For example, if you append to a vector with 100 elements, it doesn’t create a new vector 101 elements long. Only a few small objects are allocated internally.
3. Mutation Tracking
One of the advantages of immutability is that you can optimize your application by making use of reference and value equality. This makes it easy to identify if anything has changed. You can consider the example of state change in the React component. shouldComponentUpdate can be used to check if the state is identical comparing the state object and preventing it from unnecessary rendering.
Immutability allows you to track the changes that happen to these objects like a chain of events. Variables have new references that are easy to track compared to existing variables. This helps in debugging the code and building the concurrent application. Also, event debuggers help you to replay DOM events with video playbacks that work on tracking mutation.
For the first time, the concept of immutability might be confusing for you. You can do better if you understand the need and benefits of immutability. When the state is mutated you bump into many errors and dealing with it makes you much better in understanding the concept of immutability.