Quand votre schéma de base de données a aussi besoin d'un contrôle de version
Imaginez ceci : votre équipe dispose d'un pipeline CI/CD solide pour le code applicatif. Chaque pull request déclenche des tests automatisés, construit une image conteneur et déploie sur l'environnement de staging. Puis vient le déploiement en production. Le pipeline s'exécute, l'application démarre, et plante immédiatement avec une erreur de colonne introuvable. Quelqu'un a oublié d'exécuter la migration de base de données qui ajoute la colonne phone_number. Le déploiement échoue, les utilisateurs voient des erreurs, et l'équipe se démène pour comprendre ce qui s'est passé.
Ce scénario se reproduit quotidiennement dans les équipes. Le code applicatif est versionné, testé et déployé via un pipeline. Mais les modifications du schéma de base de données sont traitées comme une réflexion après coup, quelque chose que quelqu'un exécute manuellement avant ou après le déploiement. Le décalage entre le déploiement du code et les changements de schéma crée une faille par laquelle les erreurs s'infiltrent.
Le problème : comment le pipeline sait-il ce qui a été exécuté ?
Lorsque vous avez un répertoire de scripts de migration comme V001_create_users.sql, V002_add_phone.sql et V003_add_index.sql, le pipeline doit savoir lesquels ont déjà été appliqués à la base de données. Vous ne pouvez pas exécuter tous les fichiers depuis le début à chaque déploiement. La base de données de production contient déjà des données réelles. Réexécuter V001 échouerait car la table existe déjà, ou pire, supprimerait et recréerait les tables, détruisant les données clients.
Sans mécanisme de suivi, les équipes recourent à des vérifications manuelles. Quelqu'un se connecte à la base de données, exécute \dt ou SHOW TABLES, et essaie de se souvenir de ce qui a été déployé la semaine dernière. Ou ils s'appuient sur un tableur partagé que personne ne met à jour. Ou ils exécutent simplement la migration en espérant que tout se passe bien.
Aucune de ces approches ne passe à l'échelle. Elles introduisent des erreurs humaines, ralentissent les déploiements et créent de la peur autour de chaque modification de base de données.
La solution : une table de migration dans la base de données
La réponse est étonnamment simple : laissez la base de données suivre son propre historique de migrations. Créez une table spéciale, généralement nommée schema_migrations ou migration_history, qui enregistre chaque script de migration exécuté.
Voici comment cela fonctionne en pratique :
- La première fois qu'un outil de migration s'exécute sur une base de données vide, il crée la table de migration.
- Après l'exécution réussie de chaque script de migration, l'outil insère une ligne avec le nom du script et l'horodatage d'exécution.
- Lors des déploiements suivants, le pipeline lit la table de migration, la compare à la liste des fichiers de migration disponibles, et exécute uniquement les scripts qui ne sont pas encore enregistrés.
Par exemple, après l'exécution de V001_create_users.sql, la table de migration contient une ligne : V001_create_users.sql. Lors du prochain déploiement incluant V002_add_phone.sql, le pipeline vérifie la table, constate que V002 est manquant, et l'exécute. Après succès, il ajoute une nouvelle ligne. La base de données elle-même devient la source unique de vérité pour connaître la version actuelle du schéma.
Pourquoi c'est important pour votre pipeline
Ce mécanisme s'appelle le verrouillage de version. La base de données détient l'enregistrement faisant autorité de son propre état. Il n'est pas nécessaire d'avoir un fichier de configuration séparé, une variable d'environnement qui pourrait se désynchroniser, ou une liste de contrôle manuelle que quelqu'un oublie de mettre à jour.
Pour un pipeline CI/CD, c'est crucial. Le pipeline peut désormais prendre une décision objective : "Sur la base de ce que la base de données m'indique, je dois exécuter ces trois fichiers de migration." Pas de suppositions, pas de vérifications manuelles, pas de peur d'exécuter deux fois la même migration.
Le diagramme de séquence suivant illustre ce flux exact :
Différents outils de migration implémentent cela légèrement différemment. Certains enregistrent une somme de contrôle de chaque fichier de migration pour détecter si quelqu'un a modifié un script déjà exécuté. D'autres utilisent des numéros de version séquentiels au lieu de noms de fichiers. Certains outils stockent l'historique des migrations dans un schéma ou une base de données séparée. Mais le principe de base reste le même : la base de données suit son propre historique, et le pipeline lit cet historique pour déterminer les prochaines étapes.
Amorçage : la première migration
Il y a un problème de l'œuf et de la poule ici. La table de migration elle-même doit exister avant que toute autre migration puisse être enregistrée. Comment la créez-vous ?
La plupart des outils de migration gèrent cela automatiquement. Lorsque vous exécutez l'outil sur une base de données vide pour la première fois, il crée la table de migration dans le cadre de son processus d'amorçage. Certains outils enregistrent même cette action d'amorçage comme la première entrée dans l'historique des migrations.
Si vous adoptez des scripts de migration pour une base de données existante qui a déjà des tables et des données, vous avez besoin d'une approche différente. C'est là qu'une migration de base (baseline) entre en jeu. Au lieu d'essayer de recréer chaque changement historique, vous créez un seul script de migration qui capture l'état actuel du schéma de la base de données. Vous marquez ceci comme la base, et l'outil de migration l'enregistre comme déjà appliqué. À partir de ce moment, vous n'ajoutez que de nouveaux scripts de migration pour les changements à venir.
Une migration de base est une solution pragmatique. Elle reconnaît que vous ne pouvez pas réécrire l'histoire, mais vous pouvez commencer à suivre les changements à partir d'aujourd'hui. L'alternative serait de rétro-concevoir chaque modification de schéma jamais effectuée, ce qui est irréaliste pour la plupart des équipes.
Ce que la table de migration ne résout pas
La table de migration résout un problème spécifique : savoir quels scripts ont déjà été exécutés. Elle donne au pipeline un moyen fiable de déterminer la version actuelle du schéma et d'appliquer les modifications en attente.
Mais elle ne résout pas tout. La table de migration fonctionne bien pour les modifications additives comme l'ajout de tables, de colonnes ou d'index. Ces modifications ne cassent pas le code applicatif existant écrit pour l'ancien schéma. Les problèmes commencent lorsque vous devez supprimer ou renommer des colonnes, modifier des types de données ou restructurer des tables. Ces modifications destructrices ou transformatrices peuvent casser les applications en cours d'exécution, provoquer des temps d'arrêt ou corrompre des données.
La table de migration vous indique ce qui a été exécuté, mais elle ne vous dit pas si l'application en cours d'exécution est compatible avec le nouveau schéma. Cela nécessite un ensemble différent de pratiques autour des migrations rétrocompatibles, des déploiements progressifs et d'une coordination minutieuse entre le déploiement du code et les modifications du schéma.
Liste de contrôle pratique pour le suivi des migrations
- Choisissez un outil de migration qui prend en charge le suivi automatique via une table de migration.
- Assurez-vous que la table de migration est créée lors du premier déploiement, pas manuellement.
- Ne modifiez jamais un script de migration qui a déjà été appliqué en production.
- Si vous adoptez des migrations pour une base de données existante, créez d'abord une migration de base.
- Incluez l'exécution des migrations comme une étape dans votre pipeline de déploiement, pas comme un processus manuel.
- Testez les migrations dans un environnement de staging qui reflète le volume de données de production.
L'essentiel à retenir
Votre schéma de base de données est aussi important que votre code applicatif, et il mérite le même niveau de contrôle de version et d'automatisation. Une table de migration donne à votre pipeline un moyen fiable, basé sur la base de données, de savoir ce qui a été appliqué et ce qui doit encore être exécuté. Sans elle, vous faites des suppositions. Avec elle, vous avez une source unique de vérité qui élimine la source la plus courante d'échecs de déploiement liés aux modifications de base de données. Commencez à suivre vos versions de schéma dès aujourd'hui, et votre futur vous remerciera lors du prochain déploiement.