Solutions

Moderniser du code existant complexe avec des agents IA.

September 9, 2026

By Mistral

Summary

Mistral a aidé un opérateur européen du secteur de l’énergie à migrer 40 000 lignes de Fortran 77 vers C++. Découvrez comment cette migration a été menée, et les enseignements à retenir.

Les bases de code scientifiques existantes s’accumulent sur plusieurs décennies. Quand les auteurs d’origine partent, les connaissances intégrées au code deviennent difficiles à retrouver. De plus, utiliser des langages sans écosystème de développeurs actif empêche de construire à partir du travail des autres. Mistral a aidé un opérateur européen du secteur de l’énergie à migrer 40 000 lignes de Fortran 77 vers C++, un simulateur de réservoir très axé sur la physique, sans suite de tests ni documentation centralisée.

Aller au-delà de la traduction de code dans la Modernisation du code existant.

Traduire la syntaxe d’un langage vers un autre est une tâche largement résolue. Demander à n’importe quel modèle récent de traduire un extrait d’un langage qui n’est pas complètement obscur vers un autre aboutit généralement à un résultat acceptable en quelques itérations. En revanche, migrer un système complet d’un langage procédural vers du C++ orienté objet nécessite des refactorisations architecturales qui rendent la tâche non triviale.

Fortran 77 a été standardisé en 1977, comme son nom l’indique, et le code écrit dans ce langage reflète directement ces contraintes : pas de modules, pas d’espaces de noms, pas de types structurés. L’état se trouve dans des blocs COMMON, c’est-à-dire une mémoire globale partagée par l’ensemble du programme. Les variables sont typées implicitement selon leur première lettre : un nom mal orthographié crée donc silencieusement une nouvelle variable au lieu de déclencher une erreur de compilation.

Pour un exemple simple mais parlant, prenons l’implémentation suivante d’un développement de Taylor du premier degré. Les entrées et sorties sont globales dans un bloc COMMON, et IC n’est un entier que parce que son nom commence par une lettre comprise entre I et N. Les noms de variables sont assez cryptiques, car leur longueur est limitée à six caractères.

SUBROUTINE GASDEN
INCLUDE 'common.h'
DO 10 IC = 1, NCELL
10 RHOG(IC) = ROG(IV) + DROG(IV)*(P(IC)-PTAB(IV))
END

En C++, il devient possible d’utiliser des types explicites, d’écrire du code orienté objet et de renvoyer une valeur plutôt que d’écrire dans une variable globale :

double gasDensity(const GasProperties& gas, double pressure) {
size_t i = lookup(gas.pressure, pressure);
return gas.density[i] + gas.slope[i] * (pressure - gas.pressure[i]);
}

Les tableaux COMMON dispersés deviennent un unique paramètre GasProperties, et la boucle sur la grille est déplacée côté appelant. Il n’y a donc pas de correspondance ligne à ligne à vérifier, ce qui complique la vérification de la migration.

Ces différences structurelles, ainsi que l’exigence d’intégrer des frameworks modernes de calcul scientifique comme PetSc, ont soulevé plusieurs questions importantes avant même le début de la migration :

  • Comment prouver que la base de code migrée correspond numériquement à la base existante

  • Comment découper la migration en lots gérables

  • Comment utiliser au mieux des agents autonomes pour accélérer le processus

Construire un banc de test de parité avant de migrer du code existant.

Avant de laisser les agents intervenir, nous avions besoin d’un moyen de prouver que les deux bases de code concordaient. Ici, la « concordance » désignait l’égalité numérique des sorties : les résultats finaux, mais aussi un ensemble de points intermédiaires critiques signalés par les ingénieurs réservoir du client.

Nous avons ajouté :

  • des sous-programmes permettant d’exporter l’état de la base de code Fortran

  • un framework de test pour charger les points de contrôle en C++

  • Skill.md files to steer agents into using them correctly

Dans les workflows de migration, les agents ont instrumenté avec succès la base de code Fortran pour écrire des instantanés d’état et ont utilisé le framework de test C++ pour vérifier la justesse des modules migrés.

Construire d’abord cette partie du banc de test a été un investissement clairement positif pour le projet : cela a rendu les longues exécutions d’agents plus sûres, et la parité numérique constitue une preuve simple à vérifier et convaincante qu’un morceau de code a bien été migré. Nous pensons que cette étape doit faire partie des premières actions de tout projet de modernisation du code.

L’exemple ci-dessous l’illustre. Nous avons d’abord inséré une ligne dans le code Fortran pour écrire la valeur de la variable RHOG (42,71834 lors de cette exécution), puis utilisé cette même valeur comme point de contrôle de référence lors du test du module C++ migré.

Utiliser des agents d’IA pour comprendre et documenter des bases de code existantes.

La documentation du projet était dispersée entre d’anciens PDF et des commentaires enfouis dans le Fortran lui-même. L’un des plus grands bénéfices secondaires de l’effort a été de réconcilier cette documentation et de la rapprocher du code.

Heureusement, le code procédural comme Fortran présente une propriété pratique : tout le programme peut être représenté sous la forme d’un arbre appelant-appelé unique. Nous avons généré cet arbre en analysant la base de code avec un parseur personnalisé, puis utilisé la Vibe CLI pour lancer plus d’une centaine d’agents chargés de le documenter. Chaque agent pouvait récupérer les PDF pertinents via des Bibliothèques de documents et Mistral OCR.

En partant des feuilles de l’arbre et en remontant, chaque nœud lançait un sous-agent chargé de le documenter et d’ouvrir une PR vers le dépôt d’origine. Un agent relecteur exécuté en boucle selon une planification cron recherchait les PR nouvellement ouvertes, les relisait et planifiait des tâches de correction si nécessaire.

Utiliser les agents d’IA pour la modernisation du code.

Lors de notre première tentative, nous avons donné une autonomie complète aux agents : un agent par sous-programme Fortran, chacun traduisant sa fonction en C++ de manière indépendante sur une semaine. Le résultat était fonctionnel, mais on ne pouvait pas appeler cela une modernisation du code. Les blocs COMMON sont devenus des structs globales, à l’identique. Le flux de contrôle piloté par des GOTO est resté intact au lieu d’être restructuré en boucles ou en retours anticipés. Cela ressemblait à du Fortran réécrit avec une syntaxe C++, plutôt qu’à du code modernisé.

Lors de la deuxième tentative, nous avons corrigé cela en donnant aux agents une structure plutôt qu’une simple autonomie : un planificateur, un codeur, un testeur et un relecteur qualité travaillaient ensemble sur chaque module. La qualité du code s’est nettement améliorée par rapport à la première tentative. Mais la complexité du code source a fini par rattraper les agents. Ils rencontraient un bug, tentaient quelques corrections, puis se bloquaient, sans personne disponible pour intervenir.

Nous avons retenu un compromis : un humain pilotait un workflow composé d’agents codeur, testeur et relecteur, en migrant la base de code module par module. Cela conservait la qualité de code de la deuxième tentative tout en ajoutant un point de contrôle humain pour débloquer les agents lorsqu’ils restaient coincés. La section suivante décrit ce workflow en détail.

Exécuter des workflows structurés d’agents d’IA pour des migrations de code complexes.

Une fois la base de code documentée et le banc de test de parité en place, il restait à ajuster le niveau d’autonomie que nous pouvions donner aux agents tout en obtenant du code pouvant être merge. Nous avons testé les deux extrêmes, des exécutions entièrement autonomes aux sessions manuelles étroitement supervisées. Le workflow structuré ci-dessous est celui que nous avons retenu pour ce cas d’utilisation.

Avec les ingénieurs réservoir du client, nous avons utilisé l’arbre appelant-appelé pour identifier des modules indépendants : des sous-arbres autonomes de taille gérable (empiriquement, moins d’environ 10 000 lignes de Fortran). Chaque module suivait le même workflow :

  1. Générer l’architecture C++ cible.

  2. La relire avec un ingénieur réservoir.

  3. Après approbation, la découper en file de tâches.

  4. Exécuter un sous-workflow d’implémentation par tâche : planifier → implémenter → tester → répéter.

  5. Un humain relit les PR obtenues et demande des modifications jusqu’au merge.

Reconnaître les limites de la modernisation du code existant assistée par l’IA.

Le premier sprint a couvert les fonctionnalités cœur : 40 000 lignes sur 300 000. La base de code Fortran était autonome et exécutable, ce qui constitue une condition de départ favorable. Les migrations qui dépendent de systèmes externes, ne disposent pas d’une référence exécutable ou encodent une physique documentée nulle part apporteraient des défis supplémentaires qui ne sont pas abordés dans cet article.

Appliquer trois principes pour moderniser des systèmes existants complexes.

Trois enseignements de ce projet s’appliquent à toute migration importante de code existant.

  1. Construisez le banc de test de parité avant d’écrire le code de migration : la concordance numérique est la preuve la moins coûteuse et la plus convaincante qu’un module est terminé.

  2. Remettez la documentation en ordre avant de vous appuyer sur les agents, car il est impossible de migrer du code que personne ne sait lire.

  3. Et à cette échelle, les workflows structurés avec points de revue humains sont préférables à l’autonomie complète comme aux sessions manuelles pilotées de bout en bout.


Nous recrutons

L’équipe Applied AI de Mistral réunit des ingénieurs qui construisent des solutions full-stack autour des modèles Mistral et de la plateforme Entreprise. Nous livrons des solutions critiques et propres à chaque domaine pour résoudre certains des problèmes les plus difficiles.

Si ce type de travail vous intéresse, postulez pour rejoindre notre équipe.