Quand votre schéma de base de données est correct, mais vos données sont erronées

Vous venez d'exécuter une migration de base de données qui a ajouté une nouvelle colonne. Tout semblait bon. La modification du schéma a réussi, la colonne existe et l'application fonctionne. Mais quelqu'un remarque soudain : tous les utilisateurs qui se sont inscrits il y a trois ans auraient dû être marqués comme vérifiés, et ils ne le sont pas. Ou les numéros de téléphone que vous avez migrés ont maintenant des formats incohérents car les anciennes données ne respectaient pas les nouvelles règles.

Le schéma est correct. Le type de colonne correspond. Les contraintes sont valides. Le problème, ce sont les données elles-mêmes.

C'est une situation qui semble pire qu'une migration échouée. Une migration échouée est évidente. Vous voyez l'erreur, vous savez que quelque chose s'est cassé et vous pouvez agir. Mais une migration qui réussit avec de mauvaises données est subtile. Elle peut rester en production pendant des heures ou des jours avant que quelqu'un ne la remarque. Et quand vous la remarquez, l'instinct naturel est de paniquer et de chercher des moyens de tout annuler.

Mais annuler le schéma pour corriger les données crée un nouvel ensemble de problèmes. L'application pourrait ne plus fonctionner avec l'ancien schéma. Vous pourriez perdre des données qui étaient correctes. Et vous annulez un changement structurel qui était en fait juste, simplement pour corriger un contenu qui était erroné.

Le vrai problème n'est pas le schéma

Quand une migration ajoute une colonne comme is_verified avec une valeur par défaut de false, la modification du schéma est simple. La colonne existe, la valeur par défaut fonctionne et les nouveaux enregistrements se comporteront correctement. Le problème est que les utilisateurs existants qui devraient être vérifiés sont maintenant marqués comme non vérifiés. Le schéma n'a pas causé cela. La logique de migration n'a pas causé cela. L'écart était dans la compréhension de ce que les données existantes auraient dû être.

Un autre exemple courant : une migration modifie le stockage des numéros de téléphone pour exiger les indicatifs de pays. Le nouveau format est correct et les nouvelles entrées suivront les règles. Mais les anciens numéros de téléphone qui ont été stockés sans indicatif de pays sont maintenant incohérents. Le schéma est correct. Les données ne le sont pas.

Dans les deux cas, la solution n'est pas de revenir en arrière sur le schéma. La solution est de corriger les données tout en conservant le schéma intact.

Scripts de compensation : corriger les données sans toucher à la structure

Un script de compensation est une migration qui ne modifie que les données, pas le schéma. Il s'exécute comme une migration normale, passe par le même pipeline et suit le même processus de déploiement. Mais au lieu de ALTER TABLE, CREATE INDEX ou ADD COLUMN, il contient uniquement des instructions UPDATE, INSERT ou DELETE qui corrigent les données.

L'objectif est simple : amener les données à l'état correct sans modifier la structure de la table.

Voici un exemple pratique. Après avoir ajouté une colonne currency avec une valeur par défaut de 'IDR', l'équipe réalise que toutes les transactions des partenaires internationaux doivent utiliser 'USD'. Le script de compensation ressemble à ceci :

UPDATE transactions SET currency = 'USD' WHERE partner_type = 'international';

Aucune modification de schéma. Aucune nouvelle colonne. Aucune conversion de type. Juste une correction de données ciblée.

Les scripts de compensation gèrent également les échecs partiels de migration. Imaginez une migration qui crée une nouvelle table et déplace les données d'une ancienne. Certaines lignes ne parviennent pas à être transférées en raison d'une violation de contrainte. Au lieu d'annuler toute la migration et de recommencer, un script de compensation peut gérer les lignes restantes. Il insère ou met à jour uniquement les enregistrements qui ont été manqués, sans réexécuter la migration complète.

Rendre vos scripts de compensation idempotents

Il y a une règle qui prime sur toutes les autres : les scripts de compensation doivent être idempotents. Exécuter le script deux fois doit produire le même résultat que l'exécuter une fois.

Ce n'est pas une préoccupation théorique. En pratique, les migrations sont réexécutées. Un pipeline redémarre. Un environnement est actualisé. Quelqu'un exécute la migration manuellement pendant le débogage. Si votre script n'est pas idempotent, l'exécuter deux fois peut corrompre vos données.

La correction est simple. Vérifiez toujours l'état actuel avant d'apporter des modifications. Utilisez une clause WHERE suffisamment spécifique pour n'affecter que les lignes qui nécessitent une correction. Si votre base de données le supporte, utilisez des clauses ON CONFLICT pour les insertions.

Au lieu de ceci :

UPDATE transactions SET currency = 'USD' WHERE partner_type = 'international';

Écrivez ceci :

UPDATE transactions SET currency = 'USD' WHERE partner_type = 'international' AND currency IS DISTINCT FROM 'USD';

La différence est minime mais cruciale. La deuxième version ne met à jour que les lignes où la devise n'est pas déjà 'USD'. L'exécuter cent fois n'affectera que les lignes qui nécessitent une modification, et uniquement lors de la première exécution.

Quand les scripts de compensation ne suffisent pas

Les scripts de compensation ne sont pas une solution universelle. Ils fonctionnent lorsque le schéma est correct et que seules les données doivent être corrigées. Si le schéma lui-même est erroné, vous avez toujours besoin d'une migration de schéma appropriée.

Par exemple, si vous avez ajouté une colonne avec le mauvais type de données, ou si les contraintes de colonne sont trop strictes pour les données qui doivent être stockées, un script de compensation ne peut pas vous aider. Vous devez modifier le schéma. De même, si une migration a introduit un bug qui a corrompu les données d'une manière qui ne peut pas être corrigée avec de simples instructions UPDATE, vous pourriez avoir besoin d'une approche plus complexe.

Mais pour le cas courant où le schéma est correct et les données sont erronées, les scripts de compensation sont plus sûrs que toute autre alternative. Ils évitent les risques des migrations descendantes, ils ne nécessitent pas de restauration à partir de sauvegardes et ils peuvent s'exécuter pendant que l'application continue de servir le trafic.

Une liste de contrôle rapide pour écrire des scripts de compensation

  • Confirmez que le schéma est correct avant d'écrire le script. Si la structure doit changer, traitez cela d'abord.
  • Écrivez le script comme une nouvelle migration, pas comme un correctif appliqué directement à la base de données.
  • Rendez chaque instruction idempotente. Vérifiez les conditions avant de mettre à jour ou d'insérer.
  • Testez le script sur une copie des données de production, pas seulement sur une base de données vide.
  • Incluez une journalisation ou des commentaires qui expliquent pourquoi la correction des données est nécessaire, afin que les futurs membres de l'équipe comprennent le contexte.

L'essentiel à retenir

Quand une migration tourne mal, l'instinct est souvent de tout annuler. Mais annuler le schéma est une opération lourde qui peut casser l'application et perdre des données correctes. Les scripts de compensation vous offrent un outil plus léger et plus précis. Ils vous permettent de corriger les données tout en conservant le schéma qui fonctionne. La prochaine fois que vous verrez une migration qui a réussi mais a laissé derrière elle de mauvaises données, demandez-vous : le schéma est-il erroné, ou les données sont-elles erronées ? Si la réponse est les données, écrivez un script de compensation. C'est plus rapide, plus sûr et moins perturbateur que d'annuler les modifications.