DEV Community

Pando
Pando

Posted on

Mon app servait une vieille version à mes utilisateurs pendant 3 semaines — le piège du service worker published: false

Je ne suis pas développeur. Technico-commercial de formation, j'ai construit une application complète avec un agent IA : authentification, base de données, paiements, déploiement.

Et pendant trois semaines, j'ai cru que mon application était hantée.

Le symptôme

Je déployais une correction. Chez moi, elle fonctionnait. Mais certains utilisateurs continuaient de voir l'ancienne version — parfois pendant plusieurs jours.

Aucune erreur dans la console. Aucune erreur côté serveur. Le déploiement était marqué comme réussi.

Les fausses pistes

J'ai tout soupçonné, dans cet ordre :

  • Le déploiement. J'ai redéployé. Plusieurs fois.
  • Le cache de l'hébergeur. J'ai cherché comment le vider.
  • Le DNS. J'ai attendu la « propagation ».
  • Mon propre code. C'est le pire : j'ai « corrigé » du code qui fonctionnait parfaitement, puisque je pensais que mes corrections précédentes n'avaient pas marché.

Rien ne changeait, et pour une raison simple : le problème n'était dans aucun de ces endroits.

La vraie cause

Des semaines plus tôt, j'avais ajouté un service worker pour que mon application soit installable sur mobile, comme une vraie app. Un petit fichier, sw.js, d'une vingtaine de lignes.

Ce service worker mettait en cache le HTML, le JavaScript et le CSS de l'application. Et le navigateur servait ce cache sans jamais repasser par le réseau.

Autrement dit : mon serveur envoyait bien la nouvelle version. Mais le navigateur de mes utilisateurs ne la demandait jamais. Il avait déjà une copie, et il s'en contentait.

Deux mécanismes rendent ce piège particulièrement vicieux :

  1. Le navigateur ne vérifie que le fichier sw.js lui-même. Si ce fichier ne change pas entre deux déploiements, aucun nouveau service worker n'est installé — et l'ancien cache reste en place indéfiniment.
  2. Même quand un nouveau service worker arrive, il attend. Par défaut, il reste en attente tant que l'utilisateur n'a pas fermé tous les onglets de l'application. Un utilisateur qui garde un onglet ouvert en permanence peut rester sur l'ancienne version très longtemps.

Comment le diagnostiquer en deux minutes

Dans Chrome, ouvre les outils de développement (F12) :

  • Onglet ApplicationService Workers : si tu vois un service worker marqué waiting, tu tiens ton coupable.
  • Toujours dans ApplicationStorageClear site data : si l'application affiche soudain la bonne version, c'était le cache.

Si ça fonctionne après avoir vidé les données du site, le problème n'est ni ton code, ni ton hébergeur.

La correction

Supprimer le fichier sw.js ne suffit pas : les navigateurs de tes utilisateurs gardent l'ancien service worker installé. Il faut leur envoyer un service worker qui fait le ménage lui-même.

Étape 1 — Le « kill-switch », à déployer une fois pour libérer tous les utilisateurs bloqués :

// sw.js — kill-switch : nettoie tout et se désinstalle
self.addEventListener('install', () => self.skipWaiting());

self.addEventListener('activate', (event) => {
  event.waitUntil((async () => {
    const keys = await caches.keys();
    await Promise.all(keys.map((key) => caches.delete(key)));
    await self.registration.unregister();
    const clients = await self.clients.matchAll({ type: 'window' });
    clients.forEach((client) => client.navigate(client.url));
  })());
});
Enter fullscreen mode Exit fullscreen mode

skipWaiting() force l'activation immédiate, sans attendre la fermeture des onglets. Le service worker vide tous les caches, se désinstalle, puis recharge les pages ouvertes.

Étape 2 — Un service worker minimal, pour de bon. J'avais besoin de garder l'application installable. J'ai donc remplacé le kill-switch par un service worker qui ne met rien en cache, et qui purge les restes à chaque activation :

// sw.js — minimal : installabilité, aucun cache
self.addEventListener('install', () => self.skipWaiting());

self.addEventListener('activate', (event) => {
  event.waitUntil((async () => {
    const keys = await caches.keys();
    await Promise.all(keys.map((key) => caches.delete(key)));
    await self.clients.claim();
  })());
});
Enter fullscreen mode Exit fullscreen mode

Pas de gestionnaire fetch : il n'intercepte aucune requête. Le navigateur passe toujours par le réseau, et chaque déploiement est vu immédiatement.

Si tu veux vraiment un mode hors-ligne, la règle d'or : ne mets jamais ton index.html en « cache d'abord ». Versionne le nom de ton cache (cache-v2, cache-v3…) et supprime les anciens à l'activation. Et configure ton hébergeur pour que sw.js lui-même ne soit jamais mis en cache.

Ce que j'en retiens

L'IA m'a écrit ce service worker en trente secondes. Elle ne m'a pas prévenu de ce qu'il ferait trois semaines plus tard.

C'est le schéma que j'ai retrouvé encore et encore en mettant mon application en production : le code fonctionne, et le problème surgit ailleurs — dans le cache, dans les permissions de la base, dans un webhook, dans une variable d'environnement mal nommée.

J'ai fini par documenter les 30 pièges qui m'ont coûté le plus de temps, chacun avec le symptôme, la fausse piste, la vraie cause et la correction. Je les ai rassemblés dans un starter kit, avec le squelette de production qui va avec : Prêt à Prod.

Mais si tu ne retiens qu'une chose de cet article : la prochaine fois que ton déploiement « ne marche pas », vide les données du site avant de toucher à ton code.

Top comments (0)