Le coût de l’immutabilité

Le coût de l’immutabilité

12 avril 2022 0 Par Aschen

Depuis quelques années, j’entend beaucoup parlé d’immutabilité en Javascript frontend avec de nombreuses librairies (notamment Immutable.js) tentant de copier cette fonctionnalité qui nous vient des langages purement fonctionnels.

Dans cet article, nous allons parler des avantages de l’immutabilité et de son coût.

C’est quoi l’immutabilité?

L’immutabilité revient à empêcher la modification des variables existantes.

En Javascript, on peut prendre les exemples suivant par exemple:

// mutable: add property to object
let person = { name: 'Aschen' };
person.city = 'ahangama';

// immutable: create a new object
let person = { name: 'Aschen' };
person = { ...person, city: 'ahangama' };

// mutable: add an element to the array
let cities = ['minsk', 'tbilisi'];
cities.push('ahangama');

// immutable: create a new array
let cities = ['minsk', 'tbilisi'];
cities = [...cities, 'ahangama'];

Ceux qui ont lu l’article Référence vs Copie en Javascript se souviennent que le spread operator n’effectue pas un véritable clone de l’objet.

Le principal argument de l’immutabilité est d’éviter les effets de bords indésirables, prenont un autre exemple:

async function createPerson(person) {
  person.createdAt = Date.now();

  await persist(person);
}

const person = { name: 'Aschen' };
createPerson(person);
// person has been mutated here

Ici l’ajout d’une propriété à un objet par une fonction risque de créer des effets de bords sur le reste du code.

Les plus attentifs me diront qu’il s’agissait simplement de respecter une bonne pratique de programmation qui consiste à ne pas muter les variables passées en paramètres en ils auront raison!

La version immutable serait donc:

// pure function, respect immutability principle
async function createPerson(person) {
  const personWithMetadata = { ...person, createdAt: Date.now() };

  await persist(person);
}

L’argument principal de l’immutabilité dans React est la facilitation des comparaisons du DOM virtuel pour savoir à quel moment il faut render les composants.

Il est plus simple de comparer uniquement les références des objets plutôt que de les traverser et comparer leur contenu.

const personAschen = { city: 'ahangama', name: 'Aschen' };
const personDana = { ...personAschen, name: 'Dana' };

// compare references to the objects and not the content
personAschen !== personDana // true

Plus d’informations sur le processus de Réconciliation de React: https://reactjs.org/docs/reconciliation.html

Immutabilité et performances

Comme dans tout choix architecturel, le choix de l’immutabilité vient avec un tradeoff, ici ça sera les performances.

En effet, l’immutabilité implique de créer de nouveaux objets à chaque fois que vous souhaitez les modifier avec les conséquences suivantes:

  • plus d’allocations de mémoire
  • plus d’objets à « nettoyer » pour le garbage collector

Si on réalise un benchmark entre une utilisation immutable des objets et des tableaux, on remarque une très grosse différence de performances:

Code du benchmark disponible ici sur Gist

On remarque notamment qu’il est 500x plus rapide de manipuler des objets mutable plutôt qu’immutables.

Ces chiffres sont assez important mais bien sur la perte de performance reste négligeable par rapport à une requête réseau donc comme toujours il est nécessaire de bien comprendre quel à quel genre de tradeoff on a affaire pour prendre les bonnes décisions architecturales 😉


Image d’en-tête: Sigiriya Rock Mountain, Sri Lanka