Métaphore visuelle opposant un document texte plat à une riche structure de graphe interconnecté émergeant d'un code hérité, dans le contexte de la migration du COBOL vers Java.
Artificial IntelligenceSoftware EngineeringTechnology

L'IA a traduit 30 ans de COBOL à la perfection. Puis elle a corrompu la base de données.

Ashutosh SinghalAshutosh Singhal5 février 202616 min

C'était un mardi soir, et je fixais une trace de pile qui n'avait aucun sens.

Nous travaillions avec une équipe de services financiers qui tentait de migrer un module de transactions central du COBOL vers Java. L'IA avait fait son travail — du moins le pensions-nous. Le code Java généré était propre, bien structuré, et compilait sans la moindre erreur. Les tests unitaires passaient. Tout le monde sur l'appel était prudemment optimiste. Puis ils l'ont déployé dans l'environnement de test, et le premier virement bancaire a corrompu la base de données.

Le bug n'était pas dans le Java. Le Java était syntaxiquement parfait. Le bug résidait dans ce que l'IA n'avait jamais vu.

Une variable appelée TRN-LIMIT — définie non pas dans le fichier source que l'IA avait traduit, mais dans un COPYBOOK inclus des milliers de lignes plus tôt dans la chaîne d'exécution — contenait une clause REDEFINES. C'est une construction COBOL où la même adresse mémoire est interprétée comme deux types de données différents selon un indicateur défini dans un module entièrement différent. L'IA voyait TRN-LIMIT comme un simple champ numérique. Ce n'était pas le cas. C'était un décimal condensé (packed decimal) déguisé en entier selon le contexte d'exécution. L'IA a halluciné une définition standard, et l'application Java a écrit des données binaires corrompues dans une colonne de base de données.

Cette nuit-là, assis dans une salle de conférence avec mon équipe à décortiquer ce qui avait mal tourné, j'ai réalisé quelque chose qui allait refaçonner tout ce que nous avons construit chez Veriprajna : l'IA n'a pas échoué parce qu'elle était stupide. Elle a échoué parce qu'elle était aveugle.

Le problème à 1 520 milliards de dollars dont personne ne veut parler

Voici la réalité inconfortable de l'économie mondiale en 2025 : 43 % des systèmes bancaires fonctionnent encore sous COBOL, et ces systèmes traitent 95 % de toutes les transactions de distributeurs automatiques. Les logiciels qui font tourner les entreprises du Fortune 500 ? Environ 70 % ont été écrits il y a plus de vingt ans. La dette technique aux États-Unis à elle seule a gonflé jusqu'à un montant estimé à 1 520 milliards de dollars.

Et les personnes qui ont écrit ce code partent à la retraite. Pas « pourraient partir un jour » — elles partent maintenant, emportant avec elles des décennies de savoir institutionnel. Pendant ce temps, 80 % des budgets informatiques fédéraux servent à maintenir en vie les systèmes hérités, ne laissant qu'à peine 20 % pour quoi que ce soit de nouveau.

Je me suis assis en face de directeurs techniques qui décrivent leur situation de modernisation comme on décrirait une maison aux fondations qui s'effritent : vous savez qu'il faut réparer, vous savez qu'attendre aggrave les choses, mais chaque entrepreneur qui a essayé a rendu la facture plus salée sans réellement résoudre le problème.

Les chiffres le confirment. Entre 70 % et 80 % des projets de modernisation de systèmes hérités échouent à atteindre leurs objectifs. Et c'était vrai avant que l'IA générative n'entre en scène.

Pourquoi tout le monde a-t-il cru que GPT pouvait régler ça ?

Je comprends. Vraiment. Quand GPT-4 est sorti, le marché du conseil en logiciels s'est emballé. Soudain, chaque cabinet avait son « accélérateur de migration COBOL » — qui, si l'on regardait sous le capot, n'était qu'une fine surcouche autour d'un modèle de fondation. Vous collez votre paragraphe COBOL, vous récupérez une méthode Java. Magique.

Mon cofondateur et moi avons passé des semaines à évaluer ces outils. Nous leur donnions du vrai code hérité issu d'environnements clients et nous vérifiions le résultat. La syntaxe était presque toujours correcte. Le code compilait. Puis il échouait de façons incroyablement difficiles à diagnostiquer, parce que la forme du code semblait juste alors même que le sens était faux.

Le bug le plus dangereux n'est pas celui qui fait planter votre système. C'est celui qui corrompt silencieusement vos données pendant six mois avant que quiconque ne le remarque.

Le problème est architectural, et il tient à la façon dont les grands modèles de langage traitent l'information. Les LLM utilisent un mécanisme d'attention pour pondérer l'importance des différentes parties de leur entrée. Les modèles modernes se targuent de fenêtres de contexte allant jusqu'à un million de tokens. Mais la recherche a mis en évidence un phénomène appelé l'effet « Perdu au milieu » (Lost in the Middle) : les LLM présentent une courbe de performance en forme de U, se souvenant bien des informations au début et à la fin d'un prompt, mais se dégradant significativement pour tout ce qui se trouve au milieu.

Dans un projet de modernisation, un seul programme COBOL peut compter des milliers de lignes, référençant des copybooks eux-mêmes longs de milliers de lignes. Si la définition de MAX-TRANSACTION-LIMIT se trouve au milieu de ce contexte massif, l'IA a statistiquement toutes les chances de la manquer. Et quand elle manque quelque chose, elle ne s'arrête pas pour poser la question. Elle hallucine. Elle invente une définition plausible et passe à la suite.

Que se passe-t-il quand on traite le code comme du texte ?

Un schéma comparatif côte à côte montrant comment la récupération RAG vectorielle standard manque des dépendances de code critiques, par opposition à la manière dont la récupération basée sur un graphe trace les connexions logiques à travers les fichiers.

C'est l'erreur fondamentale que je vois commettre par tout l'écosystème des « surcouches d'IA », et c'est l'argument que je ne cessais d'échanger avec un investisseur potentiel au tout début. Il a regardé notre approche — construire des graphes de connaissances de dépôts de code — et a dit : « Pourquoi ne pas simplement utiliser une fenêtre de contexte plus grande ? GPT-5 réglera ça. »

J'ai ouvert un programme COBOL sur mon ordinateur portable. « Trouve-moi la définition de ACCOUNT-BALANCE », ai-je dit.

Il a fouillé le fichier. Impossible de la trouver. Parce qu'elle n'était pas dans ce fichier. Elle était dans un copybook, inclus via une instruction à la ligne 47, qui lui-même référençait une division de données partagée maintenue par une équipe complètement différente.

« Maintenant, imagine que tu es un LLM, ai-je dit. Tu effectues une recherche de similarité vectorielle pour du code lié au "traitement des paiements". Tu vas trouver cinq fragments qui mentionnent le mot "paiement". Tu vas complètement manquer le fichier appelé GlobalVarDef.cbl qui définit le taux de taxe utilisé par la logique de paiement — parce que ce fichier ne mentionne jamais le mot "paiement" nulle part. »

La génération augmentée par récupération standard, ou RAG — la technique qu'utilisent la plupart des outils de codage assistés par IA pour ajouter des connaissances aux LLM — récupère le contexte sur la base de la similarité textuelle. Elle convertit le code en vecteurs et trouve des vecteurs similaires. Cela fonctionne à merveille pour les chatbots de FAQ. C'est catastrophiquement insuffisant pour le code.

Le code n'est pas du langage naturel. « Le chat était assis sur le tapis » signifie à peu près la même chose, quoi que vous ayez lu cinquante pages plus tôt. Mais x = y + 1 ne signifie rien tant que vous ne connaissez pas les définitions, les types et les états actuels de x et y — qui pourraient être définis dans un fichier différent, un module différent, ou hérités d'une classe parente.

J'ai écrit en profondeur sur ce problème structurel dans la version interactive de notre recherche. En résumé : le logiciel n'est pas du texte. Le logiciel est un graphe.

La nuit où nous avons cessé de construire une meilleure surcouche

Il y a eu un moment — je m'en souviens clairement — où mon équipe débattait de notre architecture. Nous avions deux voies. Voie une : construire un pipeline RAG plus intelligent. De meilleurs découpages, de meilleurs plongements, de meilleurs prompts. Itérer sur l'approche par surcouche jusqu'à ce qu'elle fonctionne suffisamment bien. Voie deux : jeter entièrement le paradigme fondé sur le texte et traiter le code pour ce qu'il est réellement — un système relationnel de logique.

La voie une était plus rapide. La voie une était ce que les investisseurs comprenaient. La voie une avait déjà une douzaine de concurrents qui prouvaient la demande du marché.

Mon ingénieure principale a ouvert un tableau blanc et a dessiné un programme COBOL sous forme de graphe. Des nœuds pour les variables, les fonctions, les copybooks, les tables de base de données. Des arêtes pour CALLS, READS, UPDATES_TABLE, IMPORTS_COPYBOOK. Puis elle a tracé une chaîne de dépendances : le Module A appelle le Module B, qui modifie la Variable X, laquelle est lue par le Module C dans un répertoire complètement différent.

« Demande à une recherche vectorielle de trouver cette chaîne », a-t-elle dit.

Personne ne le pouvait.

Ce fut la nuit où nous nous sommes engagés à construire ce que nous appelons désormais un graphe de connaissances conscient du dépôt (Repository-Aware Knowledge Graph) — une base de données de graphe unifiée qui combine la structure statique du code (arbres de syntaxe abstraite, graphes d'appels) avec le sens sémantique de la logique métier (documentation, commentaires, intention des variables). Nous n'allions pas construire un meilleur traducteur. Nous allions construire une carte.

Comment transformer trente ans de COBOL en une carte ?

Un schéma de pipeline en quatre phases illustrant le processus de construction du graphe de connaissances : analyse structurelle, extraction des entités et des relations, résolution des symboles inter-dépôts, et calcul de la clôture transitive.

Le processus comporte quatre phases, et je vous épargnerai les détails d'implémentation — vous les trouverez dans notre plongée technique complète. Mais les concepts comptent, car ils expliquent pourquoi cette approche réussit là où les surcouches échouent.

Premièrement, nous analysons le code de manière structurelle, et non textuelle. Les pipelines RAG standard utilisent un « découpage naïf » — ils coupent un fichier tous les 500 tokens, séparant souvent la signature d'une fonction de son corps. Nous utilisons des analyseurs comme Tree-sitter pour générer des arbres de syntaxe abstraite, qui respectent les frontières logiques du code. Une fonction est traitée comme une unité de logique complète, et non comme un fragment de texte aléatoire.

Deuxièmement, nous extrayons les entités et les relations. Les classes, les paragraphes, les variables, les tables de base de données, les points de terminaison d'API — ceux-ci deviennent des nœuds. Les arêtes entre eux — CALLS, UPDATES_TABLE, DEFINES_VARIABLE — deviennent le tissu conjonctif. Nous pouvons désormais interroger le graphe : « Montre-moi chaque paragraphe qui met à jour le champ CUSTOMER-ID. » Des résultats exacts, instantanément. Essayez ça avec grep.

Troisièmement — et c'est là que ça devient intéressant — nous résolvons les symboles à travers l'ensemble du dépôt. Un analyseur standard voit ACCT-NUM dans le Fichier A et ACCT-NUM dans le Fichier B comme deux chaînes de caractères différentes. Notre système détermine que les deux renvoient à la même entrée dans un copybook partagé et les fusionne en un seul nœud. Nous fusionnons également la documentation avec le code : si un document d'exigences PDF décrit l'« API Utilisateur » et que le code contient une classe nommée UserAPI, le système relie l'intention à l'implémentation.

Quatrièmement, nous calculons la clôture transitive. Vous vous souvenez de la défaillance bancaire ? A dépend de B, B dépend de C, et l'IA voyait A mais manquait C. Notre graphe parcourt en profondeur — de A à B à C — pour identifier la définition racine de chaque variable. Lorsque l'IA génère du code pour le Module A, elle importe les définitions correctes depuis le Module C, même si le Module C se trouve dans un répertoire ou un dépôt entièrement différent.

Pourquoi le RAG standard échoue-t-il pour la migration de code ?

Les gens font toujours de la résistance là-dessus. « Le RAG fonctionne très bien pour le code », disent-ils. « Il suffit d'utiliser de meilleurs plongements. »

Laissez-moi vous donner trois scénarios où la recherche de similarité vectorielle s'effondre complètement :

Un développeur renomme Account en Acct. La similarité sémantique chute, même si la logique est identique. Une fonction nommée FNC-001 effectue un calcul d'intérêts mais ne contient aucun commentaire — chercher « calcul d'intérêts » ne la trouvera jamais. Et l'échec le plus courant : le RAG vectoriel récupère un test unitaire et un commentaire d'interface utilisateur qui mentionnent « paiement », mais manque la logique métier centrale parce que les noms de variables ne correspondent pas aux termes de la requête.

La récupération basée sur un graphe ne demande pas « quel texte semble similaire ? ». Elle demande « qu'est-ce qui est logiquement connecté ? » — et cette distinction, c'est la différence entre du code qui compile et du code qui fonctionne.

Ce que nous appelons GraphRAG opère sur la structure, non sur la similarité. Quand quelqu'un demande « refactorise la logique de paiement », le système utilise la recherche vectorielle pour trouver le point d'entrée — disons, le paragraphe ProcessPayment. Mais ensuite, au lieu de s'arrêter, il parcourt les arêtes du graphe. Il ramène les sous-routines via les arêtes CALLS, les définitions de variables via les arêtes READS, les copybooks via les arêtes INCLUDES. Ces pièces connectées peuvent être textuellement dissemblables mais sont logiquement inséparables.

La recherche montre que GraphRAG surpasse largement le RAG vectoriel dans le raisonnement à sauts multiples (multi-hop) — reliant des faits séparés par plusieurs étapes. En logiciel, presque chaque bug sérieux est une défaillance du raisonnement à sauts multiples. Si je modifie la logique du taux d'intérêt dans le Module A, quels écrans de reporting du Module Z se cassent ? Le RAG vectoriel ne peut pas répondre à cela. Le graphe le peut, parce qu'il parcourt la chaîne d'appels de fonctions qui les relie.

Le problème du GOTO (ou : pourquoi le COBOL fait halluciner des boucles à l'IA)

Un schéma montrant comment les sauts GOTO du COBOL sont cartographiés en arêtes dans un graphe de flux de contrôle, puis mis en correspondance par motifs avec des structures de contrôle Java appropriées (boucles, conditions, retours).

Je veux vous parler d'un défi technique spécifique qui a failli nous briser, parce qu'il illustre pourquoi ce travail est bien plus difficile qu'on ne le suppose.

Le COBOL possède une instruction GOTO. Java, non. GOTO permet à l'exécution du programme de sauter n'importe où — en avant, en arrière, au milieu d'un autre bloc. Elle crée le « code spaghetti » contre lequel tous les professeurs d'informatique vous mettent en garde. Traduire un GOTO n'est pas un problème de syntaxe. C'est un problème de topologie.

Nous avons observé trois outils d'IA commerciaux différents tenter de traduire un module COBOL avec un usage intensif de GOTO. L'un a généré un appel de fonction récursif qui aurait provoqué une StackOverflowError en production. Un autre a produit une boucle while(true) sans condition de sortie. Le troisième — mon préféré — a tout simplement inventé un flux de contrôle qui n'existait pas dans le code d'origine. Il semblait plausible. Il était complètement faux.

Notre approche : cartographier les destinations des GOTO comme des arêtes dans un graphe de flux de contrôle. Puis utiliser la reconnaissance de motifs sur le graphe. Un GOTO qui saute en arrière vers une étiquette antérieure ? C'est une boucle. Un GOTO qui saute un bloc ? C'est une condition. Un GOTO vers un paragraphe de sortie ? C'est une instruction de retour. L'IA, guidée par la structure du graphe, refactorise ces sauts en boucles while, en blocs if/else, ou en instructions break/continue.

Sans le graphe, l'IA devine. Avec le graphe, elle fait de l'ingénierie.

La différence entre un chatbot et un agent

Nous ne construisons pas de chatbots. J'ai besoin d'être clair là-dessus, parce que le marché est inondé d'outils qui vous permettent de « discuter avec votre base de code », et ce n'est pas la même chose.

Un chatbot prend votre question, l'envoie à GPT-4, et renvoie ce qui revient. Si le résultat est faux, vous le déboguez manuellement. C'est le mode de fonctionnement de chaque surcouche d'IA sur le marché.

Ce que nous déployons, ce sont des agents autonomes qui planifient, exécutent et s'auto-corrigent. L'agent analyse l'AST du fichier COBOL cible, identifie les dépendances, interroge le graphe de connaissances, génère du code Java, puis le compile dans un bac à sable (sandbox). Si le compilateur lève une erreur — « variable introuvable » — l'agent lit l'erreur, interroge le graphe pour trouver la dépendance manquante, et régénère. Ensuite, il exécute des tests unitaires dérivés des traces d'exécution COBOL d'origine pour vérifier l'équivalence comportementale.

Cette boucle de compilation-correction déplace le fardeau de la validation de l'humain vers le système. Mais — et cela compte énormément dans les secteurs réglementés — le graphe de connaissances offre une interprétabilité totale. Un développeur peut voir exactement pourquoi l'IA a pris chaque décision : « L'IA a importé com.bank.logic parce qu'elle a trouvé une dépendance à COPYBOOK-X. » Pas « fais-moi confiance, je suis une IA ». Au lieu de cela : voici la chaîne de citations pour cette logique.

Dans la banque, chaque ligne de code doit être auditable. Vous ne pouvez pas déployer une boîte noire qui a « probablement » vu juste. Vous avez besoin d'un système capable de montrer son raisonnement.

Et le code mort ?

Une chose m'a surpris : les systèmes hérités regorgent de code que plus personne n'utilise. De vieilles promotions, des produits retirés, des routines de débogage de 1997. Une IA fondée sur le texte migre tout ce qu'on lui donne — elle ne peut pas distinguer le code actif du code mort.

Notre graphe d'appels identifie les nœuds inaccessibles — des paragraphes ou des fichiers sans arête entrante, ce qui signifie que rien ne les appelle. Nous signalons ce code mort pour suppression avant que la migration ne commence. D'après notre expérience, cela réduit généralement la base de code de 20 à 30 %. Ce n'est pas une optimisation mineure. C'est éliminer un quart du travail et un quart de la surface d'attaque.

« Des fenêtres de contexte plus grandes ne régleront-elles pas ça ? »

On me pose encore cette question sans arrêt. L'hypothèse est que si GPT-5 ou Claude 4 peuvent gérer dix millions de tokens, le problème « Perdu au milieu » disparaît.

Ce ne sera pas le cas. Et voici pourquoi.

Même si la dégradation de l'attention s'améliore — et elle s'améliorera — vous faites toujours de la récupération de texte. Vous cherchez toujours des chaînes de caractères similaires au lieu de parcourir des connexions logiques. Une fenêtre de contexte d'un million de tokens n'aide en rien si la variable dont vous avez besoin est définie dans un fichier qui ne partage aucun mot-clé avec le fichier que vous traduisez. Le problème n'est pas la taille de la fenêtre. Le problème, c'est que la fenêtre regarde la mauvaise chose.

L'autre objection que j'entends : « Les graphes de connaissances coûtent cher à construire. » C'est vrai. Analyser un dépôt entier, résoudre les symboles, calculer la clôture transitive — c'est un investissement initial considérable. Mais considérez l'alternative. La migration manuelle d'un grand système COBOL coûte des dizaines de millions de dollars et prend des années. La migration par IA à base de surcouches coûte moins cher au départ mais génère un flux régulier de bugs induits par les hallucinations qui nécessitent un débogage humain coûteux. L'approche fondée sur un graphe a un coût de mise en place plus élevé et un coût de reprise nettement plus bas. Les données de McKinsey suggèrent que l'IA générative peut réduire les tâches de codage de 50 %, mais seulement si elle est déployée correctement. Nous avons constaté des gains de productivité des développeurs de 2 à 3 fois par rapport aux outils d'IA standard, précisément parce que les développeurs cessent de passer des heures à chercher où une variable est définie.

La carte est l'actif

Voici ce que j'aurais aimé comprendre au début : le graphe de connaissances n'est pas seulement un outil de migration. C'est un actif permanent.

Une fois que votre base de code existe sous forme de graphe, elle reste une représentation vivante de votre système. À mesure que le nouveau code Java évolue, le graphe se met à jour. Vous obtenez une documentation automatisée qui est toujours à jour. Vous obtenez une détection de la dérive architecturale — le système vous alerte si un nouveau code enfreint les règles de modularité que vous avez définies. Vous obtenez une analyse d'impact à la demande : « Si je modifie cette méthode, qu'est-ce qui casse ? »

La modernisation n'est pas un événement ponctuel. C'est un cycle de vie. Les organisations qui la traitent comme un projet — avec une date de début et une date de fin — sont celles qui finissent par se retrouver au point de départ dans cinq ans, noyées sous une nouvelle génération de dette technique.

Le code n'est pas du texte

La leçon à laquelle je reviens sans cesse — celle de ce mardi soir passé à fixer une trace de pile — est trompeusement simple : le code n'est pas du texte, et les outils qui le traitent comme du texte produiront des résultats qui semblent justes et se comportent mal.

Toute l'économie des « surcouches d'IA » repose sur une erreur de catégorie. Elle suppose que, parce que les LLM sont extraordinaires pour traiter le langage, ils doivent être extraordinaires pour traiter le code. Mais le code n'est pas du langage. Le code est un graphe — un système dense et interconnecté de dépendances, de flux de données et de changements d'état qui existe simultanément dans plusieurs dimensions. Tenter de le moderniser avec des outils fondés sur le texte, c'est comme naviguer dans une ville à l'aide d'une liste de noms de rues mais sans carte. Vous allez vous « perdre au milieu ».

Nous avons construit la carte. Et elle fonctionne — non pas parce que nous sommes plus intelligents que les équipes qui construisent des modèles de fondation, mais parce que nous avons posé une question différente. Elles ont demandé : « Comment faire pour que l'IA comprenne mieux le texte ? » Nous avons demandé : « Et si le problème n'était pas du texte du tout ? »

L'avenir de la modernisation des systèmes hérités n'est pas un modèle de langage plus grand. C'est un système qui comprend le logiciel de la façon dont le logiciel fonctionne réellement — comme une structure, pas comme des chaînes de caractères.

C'est le pari que nous avons fait chez Veriprajna. Chaque jour, une nouvelle organisation découvre que son code Java généré par IA compile magnifiquement et échoue catastrophiquement. Chaque jour, l'écart entre la traduction syntaxique et la compréhension sémantique devient plus coûteux à ignorer. Les organisations qui comblent cet écart ne feront pas que moderniser leur code. Elles finiront enfin par le comprendre — beaucoup d'entre elles pour la première fois.

Related Research

Also Published On