Pourquoi le rollback d'une base de données n'a rien à voir avec celui d'une application

Lorsqu'une nouvelle version de votre application plante, la solution est généralement simple. Vous appuyez sur le bouton de rollback dans votre pipeline de déploiement, et l'ancienne version revient. Le serveur arrête d'exécuter le code défectueux et lance le précédent. À moins que vous n'ayez modifié les dépendances système ou la configuration, l'application retrouve son état normal en quelques minutes.

Cela fonctionne parce que les applications sont conçues pour être sans état (stateless). Elles peuvent s'arrêter, redémarrer ou changer de version sans rien perdre de permanent. Les données qu'elles traitent proviennent des utilisateurs ou de la base de données. Si l'application tombe quelques secondes, les utilisateurs le remarquent peut-être, mais aucune donnée n'est perdue. Une fois l'ancienne version relancée, tout reprend comme si de rien n'était.

Les bases de données ne fonctionnent pas de cette façon.

La réalité stateful des bases de données

Une base de données est un composant avec état (stateful). Elle contient des données qui doivent survivre aux différentes versions de l'application. Profils utilisateurs, transactions, enregistrements de commandes, paramètres de configuration : tout cela vit dans la base de données et doit rester intact. Lorsque vous modifiez le schéma de la base de données — ajouter une colonne, modifier un type de données ou supprimer une table — la base de données enregistre ce changement de manière permanente. Les données déjà stockées ne reviennent pas automatiquement à leur forme précédente simplement parce que vous avez basculé l'application vers une version plus ancienne.

C'est la différence fondamentale qui surprend de nombreuses équipes. Elles supposent que le rollback d'une base de données fonctionne comme celui d'une application : exécuter une commande inverse, et tout revient à l'état d'avant la migration. En réalité, annuler des modifications de schéma est bien plus complexe et risqué.

Un exemple concret du problème

Imaginez que votre équipe ajoute une colonne phone_number à la table users via une migration de base de données. La migration s'exécute avec succès, et au cours des heures suivantes, les utilisateurs commencent à renseigner leurs numéros de téléphone. Puis quelqu'un découvre un bug dans le code de l'application qui lit cette colonne. L'équipe décide de faire un rollback de l'application vers la version précédente. L'application est restaurée et le bug disparaît.

Considérez la migration SQL qui introduit cette colonne :

-- Migration montante : ajouter la colonne phone_number
ALTER TABLE users ADD COLUMN phone_number VARCHAR(20) NOT NULL DEFAULT '';

-- Migration descendante : supprimer la colonne phone_number
ALTER TABLE users DROP COLUMN phone_number;

Après l'exécution de la migration montante et la saisie des numéros par les utilisateurs, exécuter la migration descendante supprimerait chacune de ces valeurs. Les données sont perdues — pas seulement cachées, mais définitivement supprimées de la table.

sequenceDiagram participant AppV2 as App v2 participant DB as Base de données participant AppV1 as App v1 Note over AppV2,AppV1: Rollback application AppV2->>DB: Utilise la colonne phone_number AppV2->>AppV2: Erreur détectée AppV2->>AppV1: Rollback vers v1 AppV1->>DB: Fonctionne (pas d'utilisation de phone_number) Note over AppV1: Succès Note over AppV2,AppV1: Rollback base de données AppV2->>DB: Ajouter colonne phone_number AppV2->>AppV2: Erreur détectée AppV2->>DB: Exécuter migration descendante DB->>DB: Supprimer colonne et données AppV1->>DB: Tentative de lecture de l'ancien schéma DB-->>AppV1: Incompatibilité code-schéma Note over AppV1: Toujours cassé

Mais la colonne phone_number est toujours dans la base de données. Les données saisies par les utilisateurs sont toujours là. Si vous exécutez maintenant une migration de rollback pour supprimer cette colonne, tous ces numéros de téléphone disparaissent. Définitivement. Et ces données sont peut-être déjà utilisées par d'autres fonctionnalités ou consultées par d'autres utilisateurs. Vous ne pouvez pas simplement les supprimer sans conséquences.

Ce risque de perte de données explique pourquoi le rollback d'une base de données ne peut pas être pris à la légère. Chaque fois qu'une migration modifie le schéma, les données peuvent être transformées, déplacées ou supprimées. Restaurer l'ancien schéma signifie restaurer l'ancienne forme des données. Sans un mécanisme garantissant que les données peuvent être ramenées exactement à leur état précédent, le rollback du schéma peut entraîner des données incohérentes ou manquantes.

Le problème d'incompatibilité code-schéma

Au-delà de la perte de données, il existe un second problème : l'incompatibilité entre le code de l'application et le schéma de la base de données.

Lorsque vous effectuez un rollback de l'application vers une version plus ancienne, cet ancien code peut ne pas reconnaître les nouvelles colonnes ou tables présentes dans la base de données. Il peut planter en essayant de lire une colonne qu'il n'attend pas, ou échouer à insérer des données à cause d'une contrainte qu'il ignore.

Inversement, si vous exécutez une migration de rollback qui supprime une colonne encore utilisée par le code actuel de l'application, l'application plantera immédiatement. Comme les déploiements d'application et les migrations de base de données sont rarement exécutés exactement au même instant, vous ne pouvez pas garantir que les deux côtés soient synchronisés pendant un rollback.

Cette incompatibilité est particulièrement dangereuse en production. Même quelques secondes d'incohérence peuvent provoquer des erreurs, des transactions échouées ou des données corrompues difficiles à récupérer.

Pourquoi la même stratégie de rollback ne s'applique pas

Le rollback d'application fonctionne parce que l'ancienne version est un état connu et stable. Le code est le même qu'avant le déploiement. L'environnement est le même. La seule chose qui change est le binaire en cours d'exécution.

Le rollback de base de données n'a pas d'« état connu et stable » de la même manière. Le schéma et les données ont avancé. Exécuter une migration inverse ne vous redonne pas exactement la même base de données qu'avant. Cela vous donne un schéma qui ressemble à l'ancien, mais avec des données qui ont peut-être été transformées, ajoutées ou supprimées de manière irréversible sans une planification minutieuse.

Certaines équipes tentent de contourner ce problème en prenant une sauvegarde complète de la base de données avant chaque migration. Cette approche a ses propres problèmes :

  • Restaurer une sauvegarde prend du temps — souvent des minutes ou des heures, pas des secondes.
  • Toutes les données écrites après la sauvegarde sont perdues.
  • D'autres services qui dépendent de la même base de données peuvent planter pendant la restauration.
  • La restauration elle-même peut échouer, vous laissant dans un état encore pire.

La voie plus sûre : le roll forward

En raison de ces risques, de nombreuses équipes expérimentées considèrent le rollback de base de données comme un dernier recours. Au lieu d'essayer d'annuler une migration échouée, elles préfèrent avancer (roll forward) : écrire une nouvelle migration qui corrige le problème sans inverser le schéma.

Par exemple, si une migration supprime accidentellement une colonne encore nécessaire, l'approche roll forward consisterait à rajouter la colonne dans une nouvelle migration, plutôt que d'essayer d'annuler la suppression. Cela maintient le schéma dans une seule direction et évite la complexité de l'inversion des transformations de données.

Le roll forward n'est pas toujours possible. Parfois, les dégâts sont trop importants, ou la correction nécessite une modification de schéma qui ne peut pas être exprimée comme une migration forward. Mais dans la plupart des cas, c'est plus sûr que de tenter un rollback qui pourrait entraîner une perte de données ou casser l'application.

Liste de contrôle pratique pour la sécurité des modifications de base de données

Avant d'exécuter une migration de base de données en production, parcourez cette liste :

  • La migration peut-elle être annulée sans perte de données ? Sinon, planifiez une stratégie de roll forward.
  • Existe-t-il une sauvegarde de la base de données prise immédiatement avant la migration ? Testez que la sauvegarde peut réellement être restaurée.
  • La migration est-elle rétrocompatible avec le code actuel de l'application ? L'ancien code doit toujours fonctionner pendant l'exécution de la migration.
  • Existe-t-il d'autres services ou bases de données qui dépendent du schéma que vous modifiez ? Coordonnez-vous avec leurs équipes.
  • Disposez-vous d'un moyen de détecter que la migration a causé un problème avant que les utilisateurs ne le signalent ? La surveillance et les alertes doivent être en place.

À retenir

Le rollback d'une base de données n'est pas un simple bouton d'annulation. C'est une opération à haut risque qui peut entraîner une perte de données, des erreurs applicatives et une indisponibilité prolongée. Traitez-le avec la même prudence que vous appliqueriez à une intervention chirurgicale : planifiez à l'avance, ayez une sauvegarde et préférez une correction forward chaque fois que possible. Le rollback le plus sûr est celui que vous n'avez jamais à exécuter.