Chronologie de la dérive : de l'optimisme à la réalité
Le 4 mars 2024, nous avons validé le design doc de migration lors d'une réunion d'architecture. L'estimation initiale reposait sur trois sprints : une semaine de préparation des schémas, une semaine de migration des données historiques, et trois jours de bascule finale. Le 18 mars, nous avons démarré la première phase de transformation des collections MongoDB en tables relationnelles. Dès le 22 mars, les premiers signaux d'alerte sont apparus lorsque l'équipe a découvert que 37 pour cent de nos documents utilisaient des structures imbriquées sur plus de quatre niveaux de profondeur. Ces structures nécessitaient une normalisation manuelle complexe que notre script automatisé ne pouvait pas gérer.
Le 5 avril, nous avons pris la décision de mettre en pause la migration complète pour adopter une approche progressive. Cette décision a marqué le début d'une architecture hybride où MongoDB continuait de servir les données historiques pendant que PostgreSQL gérait les nouvelles transactions. Entre avril et juin, nous avons maintenu deux systèmes de bases de données en parallèle, doublant notre surface d'attaque en termes d'incident count et multipliant par 2,4 notre temps moyen de résolution d'incidents. Le 12 juin, un incident critique a exposé un problème de cohérence des données entre les deux systèmes, affectant 847 comptes client pendant quatre heures. Ce n'est qu'en septembre que nous avons finalement achevé la migration complète, six mois après la date prévue.
Ce que nous croyions : hypothèses initiales et angles morts
Notre conviction principale reposait sur l'idée que MongoDB et PostgreSQL partageaient suffisamment de paradigmes pour permettre une traduction directe des schémas. Nous pensions que les documents JSON de MongoDB pouvaient être transformés en tables normalisées via un mapping automatisé défini dans un fichier de configuration. Cette hypothèse ignorait complètement la complexité des relations implicites dans nos collections. Par exemple, notre collection transactions contenait des références embarquées vers des utilisateurs, des produits, et des métadonnées de facturation, toutes stockées sous forme de sous-documents. Nous n'avions pas anticipé le temps nécessaire pour extraire ces relations et créer les clés étrangères appropriées.
Nous avions également sous-estimé l'impact des requêtes existantes sur l'architecture applicative. Notre codebase contenait 284 requêtes MongoDB utilisant des opérateurs spécifiques comme $unwind, $lookup, et des pipelines d'agrégation complexes. Nous pensions pouvoir les traduire en SQL en deux ou trois jours de travail concentré. En réalité, chaque requête nécessitait une réécriture complète, un test unitaire, et une validation de performance. Certaines requêtes d'agrégation qui prenaient 120 millisecondes sous MongoDB atteignaient 890 millisecondes sous PostgreSQL avant optimisation, déclenchant des alertes SLI sur notre p99 latency. Nous avons passé trois semaines entières à optimiser les index et à réécrire les jointures pour respecter nos SLA de performance.
- Traduction automatique des schémas via script Python générant des migrations Alembic en 48 heures maximum
- Conservation de la structure JSON dans PostgreSQL via le type JSONB sans refonte architecturale majeure
- Migration des données historiques en une seule opération de dump/restore pendant une fenêtre de maintenance nocturne
- Compatibilité immédiate de l'ORM existant (Mongoose) avec PostgreSQL moyennant changement de driver
- Absence d'impact sur les performances applicatives grâce aux capacités indexation de PostgreSQL
- Rollback possible en moins de deux heures en cas de problème critique détecté en production
Ces hypothèses révélaient un biais d'optimisme classique dans les projets de migration. Nous avions construit notre planning sur le meilleur scénario possible, sans intégrer de marge pour les découvertes techniques inattendues. Aucun membre de l'équipe n'avait d'expérience directe avec une migration de cette ampleur entre deux paradigmes de bases de données aussi différents. Nous aurions dû consulter des équipes ayant déjà effectué ce type de transition, ou engager un consultant spécialisé pour une journée d'audit avant de valider le planning. Au lieu de cela, nous avons fait confiance à notre lecture de la documentation PostgreSQL et à quelques articles de blog décrivant des migrations réussies dans des contextes très différents du nôtre.
Ce qui s'est réellement produit : la confrontation avec le système
La première semaine de migration a révélé que 64 pour cent de nos collections MongoDB ne correspondaient à aucun modèle relationnel évident. Les documents contenaient des tableaux imbriqués de profondeur variable, des champs optionnels utilisés de manière incohérente selon les versions de l'application, et des références circulaires entre collections que nous n'avions jamais documentées formellement. L'équipe a passé dix jours uniquement à cartographier les relations existantes et à décider quelles structures méritaient une normalisation complète versus un stockage JSONB simplifié. Cette phase d'analyse aurait dû précéder l'estimation initiale, pas la suivre.
Une migration n'est jamais une simple traduction technique, c'est une confrontation entre l'architecture idéale et quinze couches d'héritage accumulé.
En avril, l'architecture hybride que nous avons mise en place est devenue un fardeau opérationnel majeur. Chaque nouvelle fonctionnalité nécessitait une double implémentation : une pour MongoDB (données historiques), une pour PostgreSQL (nouvelles données). Notre deploy frequency est passée de 12 déploiements par semaine à 6, parce que chaque release devait être testée contre deux bases de données différentes. Les tests d'intégration prenaient désormais 43 minutes au lieu de 18, parce que nous devions valider la cohérence entre les deux systèmes. L'équipe a développé un sentiment de frustration croissant, l'impression de travailler deux fois plus pour le même résultat fonctionnel. Le moral a chuté, et deux développeurs seniors ont commencé à chercher d'autres opportunités pendant cette période.
Racine du problème : diagnostic technique et organisationnel
L'analyse post-incident a identifié trois causes racines distinctes mais interconnectées. Premièrement, nous avons sous-estimé la dette technique accumulée dans notre schéma MongoDB. Pendant trois ans, nous avions ajouté des champs, modifié des structures, et créé des relations ad-hoc sans jamais documenter formellement l'architecture des données. Aucun design doc existant ne décrivait la structure complète de nos collections, leurs interdépendances, ou les invariants métier qu'elles devaient respecter. Cette absence de documentation a rendu impossible toute estimation précise du travail de migration.
Deuxièmement, nous avons négligé de créer un environnement de staging représentatif avant de commencer la migration. Notre environnement de test utilisait un sous-ensemble de 10 000 documents, alors que la production en contenait 8,3 millions. Les problèmes de performance et de cohérence des données ne sont apparus qu'une fois confrontés au volume réel. Nous aurions dû provisionner un cluster PostgreSQL complet, importer l'intégralité des données de production, et exécuter une semaine de tests de charge avant de valider le planning. Cette étape aurait coûté environ deux semaines de travail, mais aurait révélé les problèmes critiques avant qu'ils n'impactent les utilisateurs.
Décisions organisationnelles qui ont amplifié le problème
Au niveau organisationnel, nous avons commis l'erreur de communiquer la date de migration comme un engagement ferme auprès des parties prenantes avant d'avoir terminé la phase d'analyse. Le directeur produit a annoncé lors d'une réunion trimestrielle que la migration serait "terminée fin mars", créant une pression artificielle sur l'équipe pour respecter un délai irréaliste. Lorsque les problèmes techniques sont apparus, nous avons d'abord essayé de les résoudre rapidement plutôt que de réévaluer ouvertement le planning. Cette dynamique a conduit à des solutions temporaires (l'architecture hybride) qui ont finalement prolongé le projet bien au-delà de ce qu'une pause complète et une réévaluation honnête auraient nécessité.
- Audit complet des schémas de données existants avec cartographie des relations et des invariants métier, documenté dans un design doc formel avant toute estimation
- Création d'un environnement de staging à échelle réelle avec volume de données représentatif et exécution d'une semaine de tests de charge
- Engagement d'un consultant externe spécialisé en migrations PostgreSQL pour revue du design doc et validation des hypothèses techniques
- Communication de fourchettes d'estimation larges (4 à 12 semaines) plutôt que dates fixes, avec points de validation hebdomadaires
- Constitution d'un runbook détaillé couvrant rollback, monitoring des métriques clés, et protocoles d'incident avant le début de la migration
Leçons retenues : ce que nous faisons différemment maintenant
Depuis cet incident, nous avons institutionnalisé plusieurs pratiques pour éviter de répéter cette erreur. Toute migration de base de données nécessite désormais un design doc approuvé par au moins deux ingénieurs seniors qui n'ont pas participé à sa rédaction. Ce document doit inclure une band-pill67 "hypothèses et risques" explicite, listant toutes les croyances non validées sur lesquelles repose le planning. Nous organisons également une session de "pre-mortem" où l'équipe imagine que le projet a échoué et travaille rétrospectivement pour identifier les causes probables. Cette technique contre-intuitive révèle souvent des angles morts que l'optimisme naturel masque lors de la planification.
Nous avons également modifié notre approche du staging. Pour tout projet d'infrastructure touchant les données, nous provisionnons maintenant un environnement miroir de la production avant de commencer le développement. Cela représente un coût initial plus élevé, mais nous a permis d'éviter trois incidents majeurs potentiels au cours des douze derniers mois. Notre lead time for changes s'est amélioré de 28 pour cent sur les projets d'infrastructure depuis l'adoption de cette pratique. Nous utilisons désormais Terraform pour définir ces environnements de manière reproductible, garantissant que staging et production restent synchronisés au niveau de la configuration.
Enfin, nous avons changé notre communication avec les parties prenantes. Plutôt que d'annoncer des dates de livraison, nous partageons maintenant des jalons de validation : "semaine 2, validation du design doc", "semaine 4, migration réussie sur 100 000 documents de test", "semaine 6, décision go/no-go basée sur les métriques de performance". Cette approche transforme le planning en série de points de décision plutôt qu'en engagement irrévocable. Elle donne également aux parties prenantes une visibilité continue sur la progression réelle, réduisant les surprises et la frustration lorsque des ajustements deviennent nécessaires. Notre taux de satisfaction des stakeholders sur les projets d'infrastructure a augmenté de 41 pour cent depuis cette transition.
Réflexions finales : accepter la complexité inhérente aux systèmes vivants
Cette migration ratée nous a appris que les systèmes de production ne sont jamais aussi simples que leur description abstraite. Chaque base de données porte l'empreinte de centaines de décisions prises dans des contextes désormais oubliés, de compromis temporaires devenus permanents, et de structures adaptées à des contraintes qui ont depuis évolué. Vouloir réduire cette complexité à un script de migration automatisé de deux semaines relevait d'une forme d'arrogance technique. Les bases de données ne sont pas des artefacts statiques qu'on peut déplacer d'un système à l'autre comme des fichiers sur un disque dur. Elles sont des organismes vivants qui ont coévolué avec l'application, et les migrer nécessite de comprendre cette histoire plutôt que de l'ignorer.
Six mois après la fin de la migration, PostgreSQL fonctionne conformément à nos attentes initiales. Les performances sont stables, les requêtes complexes s'exécutent plus rapidement grâce aux index bien optimisés, et la cohérence transactionnelle nous a permis d'implémenter plusieurs fonctionnalités métier auparavant impossibles sous MongoDB. Le résultat final valide la décision stratégique de migrer. Mais le chemin pour y parvenir aurait pu être considérablement plus court et moins douloureux si nous avions investi le temps nécessaire en amont pour comprendre véritablement ce que nous allions entreprendre. La prochaine fois que nous envisageons une migration de cette ampleur, nous commencerons par trois semaines d'analyse et de prototypage avant même de discuter d'un planning, et nous traiterons toute estimation initiale comme une hypothèse de travail plutôt qu'un engagement contractuel.

