Publier des modifications frontend sans tout casser

Vous venez de pousser une nouvelle version de votre frontend. Le build est passé, les tests sont verts, et le pipeline de déploiement affiche « succès ». Mais quand vous consultez les métriques de production, quelque chose cloche. Certains utilisateurs voient une mise en page cassée. D'autres signalent qu'un bouton ne fonctionne pas. Et quelques utilisateurs sont encore sur l'ancienne version, parce que leur navigateur n'a pas encore récupéré les nouveaux fichiers.

C'est la réalité des releases frontend. Contrairement aux services backend où vous pouvez redémarrer un serveur et basculer instantanément de version, le code frontend vit dans le navigateur de l'utilisateur. Vous ne pouvez pas forcer tout le monde à recharger la page. Vous ne contrôlez pas quand leur cache expire. Et si quelque chose tourne mal, vous ne pouvez pas simplement appuyer sur un bouton de rollback en espérant que tout le monde reçoive le correctif immédiatement.

Pourquoi les releases frontend sont différentes

Le problème central est que les assets frontend sont répartis sur des milliers de navigateurs, chacun avec son propre état de cache. Quand vous déployez une nouvelle version, certains utilisateurs l'obtiennent tout de suite, d'autres après l'expiration de leur cache, et certains peuvent rester bloqués avec une version cassée jusqu'à ce qu'ils actualisent manuellement.

Pour les frontends statiques servis via un CDN, le défi est que vous ne déployez pas sur un serveur que vous contrôlez. Vous uploadez des fichiers dans un bucket de stockage et laissez le CDN les distribuer. Le CDN peut servir les anciens fichiers à certains utilisateurs et les nouveaux à d'autres, selon les en-têtes de cache et le comportement des nœuds périphériques.

Pour les frontends rendus côté serveur (SSR), le défi est différent. Vous avez affaire à de véritables serveurs qui doivent être mis à jour sans interrompre les connexions actives. Un utilisateur qui remplit un formulaire ou navigue entre les pages ne doit pas perdre sa session parce que vous avez changé de version.

Le diagramme suivant vous aide à choisir la bonne stratégie de release en fonction de votre type de frontend et de votre tolérance au risque :

flowchart TD A[Type de frontend ?] --> B[Statique] A --> C[SSR] B --> D[Déploiement progressif via CDN] D --> E[Surveiller les erreurs] E --> F{Erreurs faibles ?} F -->|Oui| G[Augmenter le trafic] F -->|Non| H[Rollback : changer le pointeur] G --> I[Déploiement à 100%] C --> J{Tolérance au risque ?} J -->|Faible| K[Release canary] J -->|Très faible| L[Déploiement blue-green] K --> M[Déployer sur quelques instances] M --> N[Surveiller la santé] N --> O{Sain ?} O -->|Oui| P[Ajouter progressivement des instances] O -->|Non| Q[Supprimer les nouvelles instances] L --> R[Déployer l'environnement green] R --> S[Bascule du load balancer] S --> T{Problèmes ?} T -->|Oui| U[Revenir au blue] T -->|Non| V[Garder le green]

Déploiements progressifs pour les frontends statiques

La façon la plus sûre de publier un frontend statique est de contrôler combien d'utilisateurs voient la nouvelle version. La plupart des CDN prennent en charge la distribution pondérée, où vous pouvez envoyer un pourcentage des requêtes vers les nouveaux fichiers et le reste vers les anciens.

Voici comment cela fonctionne généralement :

  1. Uploadez la nouvelle version dans votre bucket de stockage avec un chemin unique, comme app-v2/ ou en utilisant des noms de fichiers basés sur le hash du contenu.
  2. Configurez votre CDN pour router 5% des requêtes vers les nouveaux fichiers.
  3. Surveillez les taux d'erreur, les temps de chargement des pages et les problèmes signalés par les utilisateurs.
  4. Si tout semble bon, augmentez progressivement le pourcentage à 25%, puis 50%, puis 100%.

Vous pouvez également utiliser des feature flags côté client. Servez la même coque d'application à tout le monde, mais chargez conditionnellement de nouveaux composants ou fonctionnalités en fonction d'un cookie, d'un paramètre d'URL ou d'un identifiant utilisateur. Cela vous donne un contrôle plus fin sur qui voit quoi, sans avoir besoin de répartir le trafic au niveau du CDN.

L'avantage clé des déploiements progressifs est que vous pouvez vous arrêter à tout moment. Si les taux d'erreur grimpent à 10%, vous ramenez le pourcentage à zéro. Les utilisateurs qui ont déjà chargé la nouvelle version pourraient encore voir des problèmes, mais vous avez limité l'impact.

Releases canary pour les frontends SSR

Pour les frontends SSR, les déploiements progressifs fonctionnent comme les déploiements canary backend. Vous exécutez plusieurs instances de votre application derrière un load balancer. Quand vous voulez publier une nouvelle version, vous la déployez sur une ou deux instances pendant que les autres continuent avec l'ancienne version.

Le load balancer est configuré pour envoyer un petit pourcentage du trafic vers les nouvelles instances. Si les health checks échouent ou si les taux d'erreur augmentent, ces instances sont retirées de la rotation. Le rollback consiste simplement à arrêter les nouvelles instances et à laisser tout le trafic revenir aux anciennes.

Cette approche fonctionne bien car les applications SSR maintiennent l'état côté serveur. La session d'un utilisateur est liée à une instance spécifique, vous devez donc vous assurer que les données de session sont stockées dans un emplacement partagé comme Redis ou une base de données. Sinon, basculer le trafic entre les versions déconnectera les utilisateurs.

Pour automatiser ce processus, vous pouvez définir un déploiement canary dans votre configuration de déploiement. Voici un exemple avec Argo Rollouts sur Kubernetes :

apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: frontend-ssr
spec:
  replicas: 10
  strategy:
    canary:
      steps:
        - setWeight: 10
        - pause: { duration: 5m }
        - setWeight: 50
        - pause: { duration: 5m }
        - setWeight: 100
      analysis:
        templates:
          - templateName: success-rate
        startingStep: 0
        args:
          - name: service-name
            value: frontend-ssr
---
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
  name: success-rate
spec:
  metrics:
    - name: error-rate
      successCondition: result < 0.01
      provider:
        prometheus:
          query: |
            sum(rate(http_requests_total{status=~"5.."}[5m])) /
            sum(rate(http_requests_total[5m]))

Cette configuration route 10% du trafic vers la nouvelle version, attend 5 minutes pour surveiller les taux d'erreur, puis passe à 50% et enfin à 100% seulement si le taux d'erreur reste inférieur à 1%.

Déploiements blue-green pour zéro temps d'arrêt

Si vous devez garantir qu'aucun utilisateur ne subisse de temps d'arrêt pendant une release, le déploiement blue-green est un choix solide. Vous maintenez deux environnements identiques : blue (version actuelle) et green (nouvelle version). Tout le trafic va vers blue. Une fois que green est prêt et passe tous les health checks, vous basculez le load balancer pour envoyer tout le trafic vers green.

Si quelque chose tourne mal après la bascule, vous revenez à blue. Le processus entier prend quelques secondes, et les utilisateurs ne voient aucune interruption tant que l'état de session est géré correctement.

L'inconvénient est que vous avez besoin de deux fois plus d'infrastructure. Pour les petites équipes ou les applications à faible trafic, cela peut être excessif. Mais pour les systèmes de production à fort trafic où même quelques secondes de temps d'arrêt coûtent de l'argent ou de la confiance, le blue-green vaut la peine.

Le vrai défi : le rollback des frontends statiques

C'est là que les choses se compliquent. Revenir en arrière sur un frontend statique est techniquement simple mais pratiquement désordonné. Vous pouvez rétablir les fichiers dans votre CDN vers l'ancienne version, mais les utilisateurs qui ont déjà téléchargé les nouveaux fichiers n'obtiendront pas automatiquement les anciens. Leur cache navigateur contient toujours la nouvelle version, et il continuera à la servir jusqu'à l'expiration du cache.

La solution est de ne jamais écraser les fichiers. Chaque version doit avoir un chemin unique, généralement basé sur un hash du contenu. Quand vous déployez, vous uploadez les nouveaux fichiers à côté des anciens. Le CDN sert la version pointée par l'application. Pour revenir en arrière, vous changez le pointeur de la nouvelle version vers l'ancienne. Les utilisateurs qui ont déjà la nouvelle version en cache continueront à l'utiliser jusqu'à ce que leur cache se vide, mais les nouveaux utilisateurs et ceux qui actualisent obtiendront l'ancienne version.

Cette approche signifie que vous devez penser au rollback avant d'en avoir besoin. Si votre pipeline est conçu pour écraser les fichiers sur place, vous vous préparez à une reprise douloureuse quand quelque chose tourne mal.

Checklist pratique pour les releases frontend

Avant de pousser votre prochaine release, parcourez cette checklist rapide :

  • Tous les chemins de fichiers sont-ils uniques par version (basés sur le hash du contenu ou versionnés) ?
  • Pouvez-vous router un petit pourcentage du trafic vers la nouvelle version ?
  • Avez-vous un moyen de surveiller les taux d'erreur et les temps de chargement des pages en temps réel ?
  • Votre plan de rollback est-il testé, pas seulement documenté ?
  • Pour SSR : l'état de session est-il stocké en dehors du serveur d'application ?
  • Pour statique : avez-vous un mécanisme pour changer le pointeur de version sans redéployer ?

Ce qui compte le plus

Les déploiements progressifs et les stratégies de rollback ne sont pas des fonctionnalités que vous pouvez ajouter après la construction du pipeline. Ils doivent être conçus dans le processus de déploiement dès le départ. Si votre pipeline ne sait que déployer une version à un seul endroit, vous aurez du mal à faire des releases canary ou des déploiements blue-green. Si vous n'avez pas de gestion du trafic au niveau du CDN ou du load balancer, vous n'avez aucun moyen de limiter l'impact d'une mauvaise release.

L'objectif n'est pas d'empêcher tous les problèmes. Les problèmes arriveront. L'objectif est de faire en sorte que, quand un problème survient, vous puissiez réagir sans panique. Une stratégie de release bien conçue vous donne le contrôle sur le rayon d'impact, un chemin de rollback clair, et la confiance pour livrer des changements fréquemment. Sans elle, chaque release ressemble à un pari.