PSA: Immutable Data can Help You Avoid Pesky Bugs
In my experience as a developer, I have come to the above conclusion. Immutability is an extremely important concept to understand, especially as your application balloons out of control.
This will be a largely anecdotal article, but my gut says many people have encountered the issues and noticed the pain of an aging application. Ever submit an item to an API and noticed a
$$hashKey property on your data model? Ever wonder why a date is formatted as
12/18/2017 when accessing an page from one route and
December 18, 2017 from another route?
These problems come as a result of being able to mutate data. Many developers, myself included, focus on a specific problem while addressing a ticket (adding a feature, fixing a bug). In my experience, this leads to a lazy solution. Date not formatted right? When the component is initialized, format it!
$$hashKey present on your data model? Create a request object from the data model and write out all 35 properties one by one until you exclude view data!
As time goes on, this leads to a mess of code. If statements galore, extra steps to interact with the API, difficult to debug problems, the list goes on.
I could probably best explain this through a story in which I reviewed code very similar to what you see below.
You can see from the above codepen that the push of the
Select... option bridges the component boundary without being coupled in the code with the exception of the selector.
The developer was attempting to make an option in a drop down be the default state of
Select... to the user. This represents a non-selected item in the dropdown. What the developer did not realize is that the
reselect library memoizes the data. If the
store reference did not change, then the
myCollection property will be returned next time the selector is executed.
Since the array reference remains the same and an item added via push, this means next time
MyDataSelector is used, it will return the same collection, but with an additional
Select... option! This presents itself in a strange way if that selector is accessed from two different pages and it only happens when
Table.component is accessed first!
The solution to this problem, and more, is to use an Immutable library. I am familiar with one library, specifically, called Seamless Immutable. Seamless Immutable presents very useful error messages when attempting to mutate the data within the immutable object. When mutating the data, it returns a new reference, such as when using the
set method on an object. If we attempted to push to an Immutable object, we would see something similar to the error below:
The push method cannot be invoked on an Immutable data structure.
This would be the first sign to the developer that something was being done wrong. There are two things that can be done from this point. We can either:
a) Add a new selector that returns an array with a
Select... option in it to be utilized throughout the app
concat to create a new array reference local to the component
In either case, we’ve solved the issue! We no longer have an array reference returned by the selector that contains the
Now, there are scenarios in which we do want a mutable object. For instance, we may want to create a data model for our view to utilize and manipulate that is derived from our store of information. Seamless Immutable provides an
asMutable method to do just that, as well.
Immutable data helps prevent some bad practices that I’ve found in developing large scale applications such as:
- Manipulating data passed into a component via bindings (React, Angular etc)
- Using the wrong iteration method (i.e. manipulating references as a way to set a property on an object, instead of using a map function)
- Being unaware of what your libraries are doing/require (i.e.
$$hashKeybeing added when not using track by in ng-repeats)
These are just some of the benefits. One other benefit is seeing the
asMutable method being used is a pretty big indicator to somebody doing a code review!
As applications become larger and the number of use cases and features increases, the more benefit Immutability provides. Consider the centralized data stores such as Redux or Angular services that are injected/utilized in multiple components.
Hopefully, this blog will help you gain some curiosity in the concept of Immutability. It has been a huge life saver when it comes to libraries such as Redux where data stores are utilized in dozens of components and needs to be manipulated for each use case in the application. I hope that the concept can work its way into yours and provide some benefit!