=

desalasworks presents:

a selection of works by steven de salas

Immutability in JavaScript, A Contrarian View

TL/DR: Immutability is more a fashion trend than a necessity in JavaScript. If you are using React it does provide a neat work-around to some confusing design choices in state management. However in most other situations it wont add enough value over the complexity it introduces, serving more to pad up a resume than to fulfill an actual client need.

I wrote this originally as a answer to a stack overflow question posted by someone who was as baffled by this JavaScript programming trend as I was.

Here is my answer to this topic, it is contrarian and may also be contentious, but bear in mind its not the only answer, its just my own point of view.

Why is immutability so important(or needed) in javascript?

Well, I’m glad you asked!

Some time ago a very talented guy called Dan Abramov wrote a javascript state management library called Redux which uses pure functions and immutability. He also made some really cool videos that made the idea really easy to understand (and sell).

The timing was perfect. The novelty of Angular was fading, and JavaScript world was ready to fixate on the latest thing that had the right degree of cool, and this library was not only innovative but slotted in perfectly with React which was being peddled by another Silicon Valley powerhouse.

Sad as it may be, fashions rule in the world of JavaScript. Now Abramov is being hailed as a demigod and all us mere mortals have to subject ourselves to the Dao of Immutability… Wether it makes sense or not.

What is wrong in mutating objects?

Nothing!

In fact programmers have been mutating objects for er… as long as there has been objects to mutate. 50+ years of application development in other words.

And why complicate things? When you have object cat and it dies, do you really need a second cat to track the change? Most people would just say cat.isDead = true and be done with it.

Doesn’t (mutating objects) make things simple?

YES! .. Of course it does!

Specially in JavaScript, which in practice is most useful used for rendering a view of some state that is maintained elsewhere (like in a database).

What if I have a new News object that has to be updated? … How do I achieve in this case? Delete the store & recreate it? Isn’t adding an object to the array a less expensive operation?

Well, you can go the traditional approach and update the News object, so your in-memory representation of that object changes (and the view displayed to the user, or so one would hope)…

Or alternatively…

You can try the sexy FP/Immutability approach and add your changes to the News object to an array tracking every historical change so you can then iterate through the array and figure out what the correct state representation should be (phew!).

I am trying to learn what’s right here. Please do enlighten me 🙂

Fashions come and go buddy. There are many ways to skin a cat.

I am sorry that you have to bear the confusion of a constantly changing set of programming paradigms. But hey, WELCOME TO THE CLUB!!

Now a couple of important points to remember with regards to Immutability, and you’ll get these thrown at you with the feverish intensity that only naivety can muster.

1) Immutability is awesome for avoiding race conditions in multi-threaded environments.

Multi-threaded environments (like C++, Java and C#) are guilty of the practice of locking objects when more than one thread wants to change them. This is bad for performance, but better than the alternative of data corruption. And yet not as good as making everything immutable (Lord praise Haskell!).

BUT ALAS! In JavaScript you always operate on a single thread. Even web workers (each runs inside a separate context). So since you can’t have a thread related race condition inside your execution context (all those lovely global variables and closures), the main point in favour of Immutability goes out the window.

(Having said that, there is an advantage to using pure functions in web workers, which is that you’ll have no expectations about fiddling with objects on the main thread.)

2) Immutability can (somehow) avoid race conditions in the state of your app.

And here is the real crux of the matter, most (React) developers will tell you that Immutability and FP can somehow work this magic that allows the state of your application to become predictable.

Of course this doesn’t mean that you can avoid race conditions in the database, to pull that one off you’d have to coordinate all users in all browsers, and for that you’d need a back-end push technology like WebSockets (more on this below) that will broadcast changes to everyone running the app.

Nor does it mean that there is some inherent problem in JavaScript where your application state needs immutability in order to become predictable, any developer that has been coding front-end applications before React would tell you this.

This rather confusing claim simply means that if you use React your application is prone to race conditions, but that immutability allows you to take that pain away. Why? Because React is special.. its been designed first and foremost as a highly optimised rendering library with state management subverted to that aim, and thus component state is managed via an asynchronous chain of events (aka “one-way data binding”) that optimize rendering but you have no control over and rely on you remembering not to mutate state directly

Given this context, its easy to see how the need for immutability has little to do with JavaScript and a lot to do with React: if have a bunch of inter-dependent changes in your spanking new application and no easy way to figure out what your state is currently at, you are going to get confused, and thus it makes perfect sense to use immutability to track every historical change.

3) Race conditions are categorically bad.

Well, they might be if you are using React. But they are rare if you pick up a different framework.

Besides, you normally have far bigger problems to deal with… Problems like dependency hell. Like a bloated code-base. Like your CSS not getting loaded. Like a slow build process or being stuck to a monolithic back-end that makes iterating almost impossible. Like inexperienced devs not understanding whats going on and making a mess of things.

You know. Reality. But hey, who cares about that?

4) Immutability makes use of Reference Types to reduce the performance impact of tracking every state change.

Because seriously, if you are going to copy stuff every time your state changes, you better make sure you are smart about it.

5) Immutability allows you to UNDO stuff.

Because er.. this is the number one feature your project manager is going to ask for, right?

6) Immutable state has lots of cool potential in combination with WebSockets

Last but not least, the accumulation of state deltas makes a pretty compelling case in combination with WebSockets, which allows for an easy consumption of state as a flow of immutable events

Once the penny drops on this concept (state being a flow of events — rather than a crude set of records representing the latest view), the immutable world becomes a magical place to inhabit. A land of event-sourced wonder and possibility that transcends time itself. And when done right this can definitely make real-time apps easier to accomplish, you just broadcast the flow of events to everyone interested so they can build their own representation of the present and write back their own changes into the communal flow.

But at some point you wake up and realise that all that wonder and magic do not come for free. Unlike your eager colleagues, your stakeholders (yea, the people who pay you) care little about philosophy or fashion and a lot about the money they pay to build a product they can sell. And the bottom line is that its harder to code for immutability and easier to break it, plus there is little point having an immutable front-end if you don’t have a back-end to support it. When (and if!) you finally convince your stakeholders that you should publish and consume events via a push techology like WebSockets, you find out what a pain it is to scale in production.

 


Now for some advice, should you choose to accept it.

A choice to write JavaScript using FP/Immutability is also a choice to make your application code-base larger, more complex and harder to manage. I would strongly argue for limiting this approach to your Redux reducers, unless you know what you are doing… And IF you are going to go ahead and use immutability regardless, then apply immutable state to your whole application stack, and not just the client-side, as you’re missing the real value of it otherwise.

Now, if you are fortunate enough to be able to make choices in your work, then try and use your wisdom (or not) and do what’s right by the person who is paying you. You can base this on your experience, on your gut, or whats going on around you (admittedly if everyone is using React/Redux then there a valid argument that it will be easier to find a resource to continue your work).. Alternatively, you can try either Resume Driven Development or Hype Driven Development approaches. They might be more your sort of thing.

In short, the thing to be said for immutability is that it will make you fashionable with your peers, at least until the next craze comes around, by which point you’ll be glad to move on.


And the reply!

Hello Steven, Yes. I had all these doubts when I considered immutable.js and redux. But, your answer is amazing! It adds lot of value and thanks for addressing every single point that I had doubts on. It’s so much more clear/better now even after working for months on immutable objects. – bozzmob

Comments

7 comments

  1. Marija says:

    THANK YOU! Finally somebody who wants to create some real product thinking how to do it in real time with real people!

  2. Peter Andrews says:

    This is a brilliant article. Why do you spoil it by using the repulsive cliche “There are 10 ways to skin a cat.” How about being a bit more original (and less offensive)?

  3. steven says:

    Updated it to `many ways to skin a cat` which is the more recognizable version. Thanks Peter, and sorry that the cat is staying, none of the alternatives were punchy enough to get my point across.

  4. Johann says:

    Well you missed the most important benefit: Tracking state changes functionally and centrally makes your code 100x easier to reason about and maintain. Dealing with immutability is an unfortunate side effect of functional architecture. Immutability is not a perk, it is something you have to deal with if you want code that is easier to maintain and reason about, basically. And IMO this is not even a subjective opinion – code where every little component can change your state without notifying anyone that made assumptions on a given state is a bug-fest. People have been writing imperative code for 50 years too and they have been dealing with the same class of bugs over and over again. Functional management of state eliminates a whole class of bugs – it is a solution, with tradeoffs. Dealing with immutability in languages not explicitly designed for that kind of thing is a con, I agree but again, it is a tradeoff. The horse that says “I can time travel” reminds me of imperative code where everyone changes state at will actually, the front-end of the app looks “good” but there are state related bugs lurking everywhere.

  5. steven says:

    Hi Johann,

    I am tired of dogma. That’s why I wrote this article.

    There is so much hype about immutability already (since React became a thing) that someone needed to provide an alternate point of view for newbie developers who are contemplating going down this rabbit hole.

    And seriously, about the bugs.. Let me pick a bone there with you: There are no thread-related race-conditions in javascript. Every execution of the function stack is atomic, so the only state-related bugs you get are the ones we introduce ourselves: by losing track of the order in which we executed our code.

    With that in mind and because the language frees us from this plague, the only way to get unpredictable state is by either making a mess of it, or (my favorite) by picking the wrong third-party library to manage it. Just ask anyone who was building webapps before React how long they used to spend on side-effects.

    So you ‘eliminate a whole class of bugs’.. that didn’t really exist to begin with! And somehow this justifies introducing a whole degree of magnitude in the complexity of your app? … Er, no thanks, I’ll keep the trade-offs.

  6. ximo says:

    I’d argue that it’s not just the programmer’s fault that race conditions happen.

    Yes, there can’t be data races (thread-related race conditons) in a single-threaded environment, but as long as you deal with asynchronicity you may experience race conditions.

    The programmer may be culpable by not handling async correctly (await is a godsend in that regard!). But you may also reach more complex scenarios where it becomes hard to maintain the _correctness_ of your app. That does depend on the complexity of your app and its interactions with the outside (users/services). The trend is more concurrency, more interactions, not less.

    I don’t agree that FP and immutability introduce more complexity. If anything, I think it makes it easier to reason about code. But I admit that it’s very different from imperative or class-based programming, you have to understand it to use it correctly. Blindly jumping on a hype train is silly, as all hype trains are silly. But that doesn’t mean that the cargo is.

    I think Rich Hickey got a lot of things right in Clojure. I think embracing time (vs pretending it doesn’t exist) is the right way forward in an increasingly distributed and concurrent world. That applies to JavaScript as well. However wonderful the event-loop is, it isn’t immune to race conditions.

  7. Francisco Barros says:

    React makes use of immutability because it is easier to track state updates on non-primitive types like this than to do deep comparisons for each `key-value` pair in an object for example. For that reason, I favor immutability in React (frontend) in most (but not all situations).

    Other than that, I am completely against immutability in JavaScript in Node.js frameworks like Express, NestJS, etc… Because it brings no real benefit, makes you write more boilerplate or use 3rd party libs, makes your code slower and you should get no benefit at all from it.

    If I want immutability on the backend I will do my services in Phoenix (Elixir).

    Good to know I am not solo on this boat.

Leave a Reply

(Spamcheck Enabled)