Quand les migrations de base de données tournent mal : Rollback vs Roll-Forward

Votre équipe vient d'exécuter une migration de base de données en production. Cinq minutes plus tard, le tableau de bord de monitoring devient rouge. Les taux d'erreur grimpent. Les utilisateurs signalent des problèmes. Vous devez maintenant prendre une décision rapidement : annulez-vous le changement, ou poussez-vous un autre correctif en avant ?

Ce moment sépare les équipes qui ont un plan de celles qui paniquent. Et la réponse n'est jamais aussi simple que « il suffit de faire un rollback ». Le type de changement que vous avez effectué, les données impliquées et l'état de votre système déterminent tous quelle voie est la plus sûre.

Les deux voies de récupération

Il existe fondamentalement deux façons de se remettre d'une mauvaise migration de base de données. Elles fonctionnent différemment, comportent des risques différents et s'appliquent à des situations différentes.

Rollback signifie inverser la migration que vous venez d'exécuter. Vous exécutez la migration descendante, qui est exactement l'opposé de ce que vous avez fait. Si vous avez ajouté une colonne, la migration descendante la supprime. Si vous avez changé un type de données, la migration descendante le rétablit.

Roll-forward signifie laisser la migration problématique en place et écrire une nouvelle migration qui corrige le problème. Vous ne revenez pas en arrière. Vous avancez avec une correction.

Les deux stratégies ont leur place. L'astuce est de savoir laquelle correspond à votre situation avant d'en avoir besoin.

Quand faire un rollback

Le rollback fonctionne mieux pour les changements qui peuvent être inversés en toute sécurité. Il s'agit généralement d'opérations non destructives où l'annulation du changement n'entraîne pas de perte ou de corruption de données.

Les bons candidats pour un rollback incluent :

  • Ajouter une colonne nullable
  • Créer un nouvel index
  • Ajouter une nouvelle table
  • Créer une vue ou une fonction

Ces changements sont additifs. Lorsque vous les inversez, vous supprimez quelque chose qui a été ajouté. Aucune donnée existante n'est perdue ou corrompue dans le processus.

Considérez un scénario où vous avez ajouté une colonne last_login_at à votre table users. La colonne est nullable, donc les lignes existantes sont correctes. Après le déploiement, vous découvrez que le code de l'application a un bug qui écrit des horodatages incorrects. Revenir en arrière en supprimant la colonne est sûr. Aucune donnée n'est endommagée car la colonne était vide ou contenait des données que vous n'avez pas besoin de conserver.

Quand faire un roll-forward

Le roll-forward devient le meilleur choix lorsque la migration est destructive ou que son inversion causerait plus de dégâts que le problème d'origine.

Situations où le roll-forward est plus sûr :

  • Supprimer une colonne ou une table
  • Modifier un type de données d'une manière qui perd en précision
  • Modifier des valeurs de données existantes à grande échelle
  • Fusionner des tables ensemble
  • Supprimer une contrainte NOT NULL dont d'autres systèmes dépendent

Imaginez que vous ayez exécuté une migration qui a supprimé une colonne legacy_status. Les données de cette colonne sont perdues. Écrire une migration descendante qui rajoute la colonne ne restaurera pas les données. Les utilisateurs qui dépendaient de ce champ de statut voient maintenant des valeurs nulles. Votre meilleure action est d'écrire une nouvelle migration qui recrée la colonne et la remplit à partir d'une sauvegarde ou des logs d'application.

Un autre cas courant : vous avez changé une colonne de VARCHAR à INTEGER, convertissant des valeurs chaîne en nombres. Revenir en arrière en changeant le type en VARCHAR est risqué car les valeurs entières peuvent ne pas se reconvertir proprement en chaînes. Une valeur de 42 devient "42", mais qu'en est-il des valeurs qui ont été tronquées ou arrondies lors de la conversion ? Vous avez perdu de l'information. Le roll-forward vous permet d'écrire une migration prudente qui gère ces cas particuliers explicitement.

Écrire des migrations descendantes qui fonctionnent réellement

Si vous choisissez de supporter le rollback, vos migrations descendantes nécessitent une attention particulière. Elles ne peuvent pas être des inversions mécaniques de la migration ascendante. Chaque migration descendante doit tenir compte des données qui existent au moment du rollback.

Voici ce qui rend une migration descendante dangereuse :

Considérons un exemple concret. Vous avez ajouté une colonne nullable last_login_at à la table users, mais le code de l'application a un bug. Une migration descendante sûre et un correctif roll-forward ressembleraient à ceci :

-- Migration descendante sûre : supprimer la colonne, mais seulement après avoir vérifié que c'est sûr
BEGIN;

-- Étape 1 : Vérifier qu'aucun code d'application ou vue ne dépend de cette colonne
-- (Cette vérification est effectuée dans le pipeline de déploiement, pas en SQL)

-- Étape 2 : Supprimer la colonne
ALTER TABLE users DROP COLUMN IF EXISTS last_login_at;

COMMIT;

-- Migration roll-forward : ajouter la colonne avec le nom correct
BEGIN;

-- Ajouter la colonne avec le nom et le type prévus
ALTER TABLE users ADD COLUMN last_login_at TIMESTAMP;

-- Optionnellement, remplir à partir des logs d'application ou d'une sauvegarde
-- UPDATE users SET last_login_at = ... WHERE id IN (...);

COMMIT;

La migration descendante est sûre car la colonne est nullable et additive. La migration roll-forward corrige le problème sans inverser le changement de schéma.

  • Elle suppose que les données sont dans le même état qu'au moment où la migration ascendante a été exécutée
  • Elle ignore les lignes qui ont été ajoutées ou modifiées après la migration ascendante
  • Elle inverse aveuglément les changements de schéma sans vérifier l'intégrité des données

Une migration descendante sûre pour l'ajout d'une colonne NOT NULL avec une valeur par défaut doit :

  1. Vérifier que la suppression de la colonne ne cassera pas les requêtes de l'application
  2. Gérer toutes les lignes qui ont été insérées après l'ajout de la colonne
  3. S'assurer qu'aucune relation de clé étrangère ne dépend de la colonne

Pour un changement de type de données de VARCHAR à INTEGER, la migration descendante doit gérer les valeurs qui n'ont pas de représentation sous forme de chaîne propre. Vous pourriez avoir besoin de convertir les entiers en chaînes, mais aussi de gérer les NULL et les cas particuliers que les valeurs chaîne d'origine n'avaient pas.

Les vrais risques que vous ne pouvez pas ignorer

Le rollback semble simple, mais il comporte des risques sérieux que les équipes découvrent seulement après que quelque chose a mal tourné.

La perte de données est le plus grand risque. Lorsqu'une migration supprime une colonne, les données sont perdues. Aucune migration descendante ne peut les ramener à moins d'avoir une sauvegarde. Si vous n'avez pas pris de sauvegarde avant la migration, le rollback signifie accepter une perte de données permanente.

Les dépendances de migration créent des pièges cachés. Si la migration deux dépend d'une colonne ajoutée par la migration un, revenir en arrière avant la migration un casse tout. Votre application pourrait planter car elle s'attend à des colonnes qui n'existent plus. Vos données pourraient devenir incohérentes car des lignes référencent des valeurs qui ont été supprimées.

Le roll-forward a ses propres risques. Le plus grand est le temps. Vous devez écrire une nouvelle migration, la faire passer par le pipeline et la déployer. Pendant ce temps, votre application fonctionne avec l'état cassé. Les utilisateurs rencontrent des erreurs. Votre équipe est sous pression pour corriger rapidement, ce qui augmente la probabilité de commettre une autre erreur.

Le roll-forward nécessite également une connaissance précise de l'état actuel de la base de données. Vous ne pouvez pas écrire le correctif en vous basant sur des hypothèses. Vous devez savoir exactement à quoi ressemblent les données maintenant, pas à quoi elles ressemblaient lorsque la migration a été conçue.

Prendre la décision avant d'en avoir besoin

Le pire moment pour décider entre rollback et roll-forward est lorsque la production est en feu. À ce moment-là, vous êtes stressé, le temps presse et votre jugement est compromis.

Une meilleure approche consiste à classer chaque migration avant qu'elle ne soit exécutée. Attribuez à chaque migration une catégorie de récupération :

  • Sûr pour le rollback : Changements additifs comme les nouvelles colonnes, tables ou index
  • Nécessite une sauvegarde avant rollback : Changements qui modifient des données existantes ou suppriment des colonnes nullables
  • Roll-forward uniquement : Changements destructifs comme la suppression de colonnes, le changement de types de données ou la fusion de tables

Documentez cette classification dans vos fichiers de migration ou dans votre runbook de déploiement. Quand quelque chose tourne mal, votre équipe lit la classification et exécute la stratégie prédéfinie. Pas de débat. Pas de secondes pensées.

Une liste de décision rapide

Avant d'exécuter une migration en production, posez-vous ces questions :

L'arbre de décision suivant peut vous aider à appliquer la liste sous pression :

flowchart TD A[La migration échoue] --> B{Changement additif ?} B -- Oui --> C[Envisager le rollback] B -- Non --> D[Choisir le roll-forward] C --> E{Risque de perte de données ?} E -- Faible --> F[Rollback sûr] E -- Élevé --> G[Roll-forward à la place] D --> H{Synchronisation code-schéma ?} H -- Oui --> I[Écrire une migration de correction] H -- Non --> J[Corriger le code d'abord, puis migrer]
  • Le changement est-il additif ou destructif ?
  • La migration descendante peut-elle restaurer l'état précédent exact, y compris les données ?
  • Avez-vous une sauvegarde vérifiée prise avant la migration ?
  • La migration descendante a-t-elle été testée dans un environnement de staging ?
  • Cette migration dépend-elle d'autres migrations qui ont été exécutées avant elle ?
  • Quel est le coût du temps d'arrêt pendant que vous écrivez un correctif roll-forward ?

Si vous ne pouvez pas répondre à toutes ces questions, n'exécutez pas encore la migration en production.

L'essentiel à retenir

Le rollback et le roll-forward ne sont pas des stratégies interchangeables. Elles s'appliquent à différents types de changements et comportent des risques différents. Les équipes qui gèrent bien les incidents de base de données ne sont pas celles qui tapent le plus vite du SQL. Ce sont celles qui ont pensé à la récupération avant que la migration ne soit exécutée. Elles ont classé leurs changements, testé leurs migrations descendantes et eu une sauvegarde prête. Quand le tableau de bord est devenu rouge, elles n'ont pas paniqué. Elles ont exécuté le plan qu'elles avaient déjà établi.