
De AI vertaalde 30 jaar COBOL feilloos. Toen beschadigde hij de database.
Het was een dinsdagavond en ik staarde naar een stacktrace die nergens op sloeg.
We hadden samengewerkt met een team van financiële dienstverlening dat een kerntransactiemodule van COBOL naar Java probeerde te migreren. De AI had zijn werk gedaan — of dat dachten we tenminste. De gegenereerde Java-code was schoon, goed gestructureerd en compileerde zonder één enkele fout. De unittests slaagden. Iedereen aan de lijn was voorzichtig optimistisch. Toen zetten ze het uit naar de testomgeving, en de eerste overboeking beschadigde de database.
De bug zat niet in de Java. De Java was syntactisch perfect. De bug zat in wat de AI nooit heeft gezien.
Een variabele genaamd TRN-LIMIT — gedefinieerd niet in het bronbestand dat de AI vertaalde, maar in een COPYBOOK die duizenden regels eerder in de uitvoeringsketen was opgenomen — bevatte een REDEFINES-clausule. Dat is een COBOL-constructie waarbij hetzelfde geheugenadres als twee verschillende datatypes wordt geïnterpreteerd, afhankelijk van een vlag die in een geheel andere module is gezet. De AI zag TRN-LIMIT als een eenvoudig numeriek veld. Dat was het niet. Het was een packed decimal die zich voordeed als een integer, afhankelijk van de runtimecontext. De AI hallucineerde een standaarddefinitie, en de Java-applicatie schreef beschadigde binaire data naar een databasekolom.
Die avond, terwijl ik met mijn team in een vergaderzaal zat te ontleden wat er was misgegaan, besefte ik iets dat alles wat we bij Veriprajna bouwden zou hervormen: de AI faalde niet omdat hij dom was. Hij faalde omdat hij blind was.
Het probleem van 1,52 biljoen dollar waar niemand over wil praten
Dit is de ongemakkelijke realiteit van de wereldeconomie in 2025: 43% van de banksystemen draait nog steeds op COBOL, en die systemen verwerken 95% van alle geldautomaattransacties. De software die Fortune 500-bedrijven draaiende houdt? Ongeveer 70% daarvan is meer dan twee decennia geleden geschreven. De technische schuld in alleen de VS is opgelopen tot naar schatting 1,52 biljoen dollar.
En de mensen die deze code hebben geschreven, gaan met pensioen. Niet "gaan misschien ooit met pensioen" — ze vertrekken nu, en nemen decennia aan institutionele kennis met zich mee. Ondertussen gaat 80% van de federale IT-budgetten naar het in leven houden van legacysystemen, waardoor er amper 20% overblijft voor iets nieuws.
Ik heb tegenover CTO's gezeten die hun modernisatiesituatie beschrijven zoals je een huis met een afbrokkelende fundering zou beschrijven: je weet dat je het moet repareren, je weet dat wachten het erger maakt, maar elke aannemer die het heeft geprobeerd, heeft het duurder gemaakt zonder het probleem daadwerkelijk op te lossen.
De cijfers bevestigen dit. Tussen de 70% en 80% van de legacymodernisatieprojecten haalt zijn doelstellingen niet. Dat was al waar vóór generatieve AI in beeld kwam.
Waarom dacht iedereen dat GPT dit kon oplossen?
Ik snap het. Echt waar. Toen GPT-4 verscheen, schoot de markt voor softwareconsultancy in een hogere versnelling. Opeens had elk bureau een "COBOL-migratieversneller" — die, als je onder de motorkap keek, een dunne wrapper rond een foundation model was. Plak je COBOL-paragraaf erin, krijg een Java-methode terug. Magie.
Mijn medeoprichter en ik hebben weken besteed aan het evalueren van deze tools. We voerden ze echte legacycode uit klantomgevingen en controleerden de output. De syntaxis was bijna altijd correct. De code compileerde. En dan faalde het op manieren die ongelooflijk moeilijk te diagnosticeren waren, omdat de vorm van de code er goed uitzag, zelfs wanneer de betekenis verkeerd was.
De gevaarlijkste bug is niet degene die je systeem laat crashen. Het is degene die zes maanden lang stilletjes je data beschadigt voordat iemand het merkt.
Het probleem is architecturaal, en het komt neer op hoe grote taalmodellen informatie verwerken. LLM's gebruiken een attention-mechanisme om het belang van verschillende delen van hun input te wegen. Moderne modellen pronken met contextvensters van tot een miljoen tokens. Maar onderzoek heeft een fenomeen aangetoond dat het "Lost in the Middle"-effect wordt genoemd: LLM's vertonen een U-vormige prestatiecurve, waarbij ze informatie aan het begin en einde van een prompt goed onthouden, maar aanzienlijk verslechteren voor alles wat in het midden staat.
In een modernisatieproject kan een enkel COBOL-programma duizenden regels lang zijn, verwijzend naar copybooks die zelf duizenden regels lang zijn. Als de definitie van MAX-TRANSACTION-LIMIT in het midden van die enorme context staat, is de kans statistisch groot dat de AI het mist. En wanneer hij iets mist, stopt hij niet en vraagt niet. Hij hallucineert. Hij verzint een plausibele definitie en gaat verder.
Wat gebeurt er als je code als tekst behandelt?

Dit is de kernfout die ik het hele "AI-wrapper"-ecosysteem zie maken, en het is het argument dat ik in het begin telkens had met een potentiële investeerder. Hij keek naar onze aanpak — het bouwen van kennisgrafieken van coderepositories — en zei: "Waarom niet gewoon een groter contextvenster gebruiken? GPT-5 lost dit wel op."
Ik opende een COBOL-programma op mijn laptop. "Zoek voor mij de definitie van ACCOUNT-BALANCE," zei ik.
Hij doorzocht het bestand. Kon het niet vinden. Omdat het niet in dat bestand stond. Het stond in een copybook, opgenomen via een statement op regel 47, dat zelf verwees naar een gedeelde datadivisie die door een compleet ander team werd onderhouden.
"Stel je nu voor dat je een LLM bent," zei ik. "Je doet een vector-similarity-zoekopdracht naar code die verband houdt met 'betalingsverwerking.' Je vindt vijf fragmenten die het woord 'betaling' noemen. Je mist volledig het bestand genaamd GlobalVarDef.cbl dat het belastingtarief definieert dat door de betalingslogica wordt gebruikt — omdat dat bestand het woord 'betaling' nergens noemt."
Standaard Retrieval-Augmented Generation, oftewel RAG — de techniek die de meeste AI-codetools gebruiken om kennis aan LLM's toe te voegen — haalt context op op basis van tekstuele gelijkenis. Het zet code om in vectoren en vindt vergelijkbare vectoren. Dit werkt prachtig voor FAQ-chatbots. Het is catastrofaal ontoereikend voor code.
Code is geen natuurlijke taal. "De kat zat op de mat" betekent ongeveer hetzelfde, ongeacht wat je vijftig pagina's eerder hebt gelezen. Maar x = y + 1 betekent niets tenzij je de definities, types en huidige toestanden kent van x en y — die mogelijk in een ander bestand, een andere module, of geërfd van een bovenliggende klasse gedefinieerd zijn.
Ik heb over dit structurele probleem uitgebreid geschreven in de interactieve versie van ons onderzoek. De korte versie: software is geen tekst. Software is een grafiek.
De avond dat we stopten met een betere wrapper bouwen
Er was een moment — ik herinner het me duidelijk — waarop mijn team over onze architectuur debatteerde. We hadden twee paden. Pad één: een slimmere RAG-pijplijn bouwen. Betere chunking, betere embeddings, betere prompts. Blijven itereren op de wrapper-aanpak tot hij goed genoeg werkte. Pad twee: het op tekst gebaseerde paradigma volledig overboord gooien en code behandelen als wat het werkelijk is — een relationeel systeem van logica.
Pad één was sneller. Pad één was wat investeerders begrepen. Pad één had al een dozijn concurrenten die de marktvraag bewezen.
Mijn hoofdingenieur pakte een whiteboard en tekende een COBOL-programma als een grafiek. Nodes voor variabelen, functies, copybooks, databasetabellen. Edges voor CALLS, READS, UPDATES_TABLE, IMPORTS_COPYBOOK. Vervolgens traceerde ze een afhankelijkheidsketen: Module A roept Module B aan, die Variabele X wijzigt, die wordt gelezen door Module C in een compleet andere directory.
"Vraag een vectorzoekopdracht om die keten te vinden," zei ze.
Niemand kon het.
Dat was de avond waarop we ons committeerden aan het bouwen van wat we nu een Repository-Aware Knowledge Graph noemen — een uniforme grafiekdatabase die de statische structuur van code (abstract syntax trees, call graphs) combineert met de semantische betekenis van bedrijfslogica (documentatie, commentaar, intentie van variabelen). We gingen geen betere vertaler bouwen. We gingen een kaart bouwen.
Hoe zet je dertig jaar COBOL om in een kaart?

Het proces heeft vier fasen, en ik bespaar je de implementatiedetails — die kun je vinden in onze volledige technische deep-dive. Maar de concepten doen ertoe, want ze verklaren waarom deze aanpak werkt waar wrappers falen.
Ten eerste parsen we code structureel, niet tekstueel. Standaard RAG-pijplijnen gebruiken "naïeve splitsing" — ze knippen een bestand elke 500 tokens, waarbij ze vaak een functiehandtekening van zijn body afsnijden. Wij gebruiken parsers zoals Tree-sitter om Abstract Syntax Trees te genereren, die de logische grenzen van code respecteren. Een functie wordt behandeld als een complete eenheid van logica, niet als een willekeurige tekstspanne.
Ten tweede extraheren we entiteiten en relaties. Klassen, paragrafen, variabelen, databasetabellen, API-endpoints — deze worden nodes. De edges tussen hen — CALLS, UPDATES_TABLE, DEFINES_VARIABLE — worden het bindweefsel. We kunnen de grafiek nu bevragen: "Toon me elke paragraaf die het CUSTOMER-ID-veld bijwerkt." Exacte resultaten, direct. Probeer dat maar eens met grep.
Ten derde — en hier wordt het interessant — resolven we symbolen over de hele repository. Een standaard parser ziet ACCT-NUM in Bestand A en ACCT-NUM in Bestand B als twee verschillende strings. Ons systeem bepaalt dat beide verwijzen naar dezelfde entry in een gedeelde copybook en voegt ze samen tot één node. We voegen ook documentatie samen met code: als een PDF-vereistendocument de "User API" beschrijft en de code een klasse genaamd UserAPI bevat, koppelt het systeem intentie aan implementatie.
Ten vierde berekenen we de transitieve afsluiting. Herinner je je de bankstoring? A hangt af van B, B hangt af van C, en de AI zag A maar miste C. Onze grafiek doorloopt diep — A naar B naar C — om de wortelbepaling van elke variabele te identificeren. Wanneer de AI code genereert voor Module A, importeert hij de juiste definities uit Module C, zelfs als Module C in een andere directory of zelfs een andere repository staat.
Waarom faalt standaard RAG voor codemigratie?
Mensen brengen hier altijd tegenin. "RAG werkt prima voor code," zeggen ze. "Gebruik gewoon betere embeddings."
Laat me je drie scenario's geven waarin vector-similarity-zoekopdrachten volledig stuklopen:
Een ontwikkelaar hernoemt Account naar Acct. De semantische gelijkenis daalt, ook al is de logica identiek. Een functie genaamd FNC-001 voert renteberekening uit maar bevat geen commentaar — zoeken naar "renteberekening" zal het nooit vinden. En de meest voorkomende storing: vector-RAG haalt een unittest en een UI-commentaar op die "betaling" noemen, maar mist de kernbedrijfslogica omdat de variabelenamen niet overeenkomen met de zoektermen.
Grafiekgebaseerde retrieval vraagt niet "welke tekst lijkt op elkaar?" Het vraagt "wat is logisch verbonden?" — en dat onderscheid is het verschil tussen code die compileert en code die werkt.
Wat wij GraphRAG noemen, werkt op structuur, niet op gelijkenis. Wanneer iemand vraagt "refactor de betalingslogica," gebruikt het systeem vectorzoekopdrachten om het toegangspunt te vinden — bijvoorbeeld de ProcessPayment-paragraaf. Maar dan, in plaats van te stoppen, doorloopt het de edges van de grafiek. Het haalt de subroutines binnen via CALLS-edges, de variabeledefinities via READS-edges, de copybooks via INCLUDES-edges. Deze verbonden stukken zijn misschien tekstueel ongelijk maar logisch onlosmakelijk.
Onderzoek toont aan dat GraphRAG aanzienlijk beter presteert dan vector-RAG in multi-hop-redeneren — het verbinden van feiten die door meerdere stappen gescheiden zijn. In software is bijna elke serieuze bug een falen van multi-hop-redeneren. Als ik de rentelogica in Module A wijzig, welke rapportageschermen in Module Z breken dan? Vector-RAG kan dit niet beantwoorden. De grafiek wel, omdat hij de keten van functieaanroepen doorloopt die ze verbindt.
Het GOTO-probleem (of: waarom COBOL AI lussen laat hallucineren)

Ik wil je vertellen over één specifieke technische uitdaging die ons bijna brak, omdat het illustreert waarom dit werk zoveel moeilijker is dan mensen aannemen.
COBOL heeft een GOTO-statement. Java niet. GOTO laat de programma-uitvoering overal heen springen — vooruit, achteruit, midden in een ander blok. Het creëert de "spaghetticode" waarvoor elke informaticaprofessor je waarschuwt. Het vertalen van GOTO is geen syntaxisprobleem. Het is een topologieprobleem.
We zagen drie verschillende commerciële AI-tools proberen een COBOL-module met zwaar GOTO-gebruik te vertalen. Eén genereerde een recursieve functieaanroep die een StackOverflowError in productie zou hebben veroorzaakt. Een andere produceerde een while(true)-lus zonder exitconditie. De derde — mijn persoonlijke favoriet — verzon simpelweg een controlestroom die niet in de originele code bestond. Het zag er plausibel uit. Het was volledig verkeerd.
Onze aanpak: map de GOTO-bestemmingen als edges in een Control Flow Graph. Gebruik vervolgens patroonherkenning op de grafiek. Een GOTO die terugspringt naar een eerder label? Dat is een lus. Een GOTO die een blok overslaat? Dat is een conditie. Een GOTO naar een exit-paragraaf? Dat is een return-statement. De AI, geleid door de grafiekstructuur, refactort deze sprongen tot while-lussen, if/else-blokken, of break/continue-statements.
Zonder de grafiek raadt de AI. Met de grafiek engineert hij.
Het verschil tussen een chatbot en een agent
We bouwen geen chatbots. Ik moet daar duidelijk over zijn, want de markt wordt overspoeld met tools waarmee je kunt "chatten met je codebase," en dat is niet hetzelfde.
Een chatbot neemt je vraag, stuurt hem naar GPT-4 en geeft terug wat er terugkomt. Als de output verkeerd is, debug je het handmatig. Dat is de workflow voor elke AI-wrapper op de markt.
Wat wij inzetten zijn autonome agents die plannen, uitvoeren en zichzelf corrigeren. De agent analyseert de AST van het doel-COBOL-bestand, identificeert afhankelijkheden, bevraagt de kennisgrafiek, genereert Java-code en compileert het vervolgens in een sandbox. Als de compiler een fout gooit — "variabele niet gevonden" — leest de agent de fout, bevraagt de grafiek voor de ontbrekende afhankelijkheid en genereert opnieuw. Vervolgens draait hij unittests afgeleid van de originele COBOL-uitvoeringstraces om gedragsequivalentie te verifiëren.
Deze compile-fix-lus verschuift de validatielast van de mens naar het systeem. Maar — en dit is enorm belangrijk in gereguleerde sectoren — de kennisgrafiek biedt volledige interpreteerbaarheid. Een ontwikkelaar kan precies zien waarom de AI elke beslissing nam: "De AI importeerde com.bank.logic omdat hij een afhankelijkheid van COPYBOOK-X vond." Niet "vertrouw me, ik ben AI." Maar in plaats daarvan: hier is de citatieketen voor deze logica.
In het bankwezen moet elke regel code auditeerbaar zijn. Je kunt geen black box inzetten die het "waarschijnlijk" goed had. Je hebt een systeem nodig dat zijn werk kan laten zien.
Hoe zit het met dode code?
Eén ding dat me verraste: legacysystemen zitten vol met code die niemand meer gebruikt. Oude promoties, uitgefaseerde producten, debug-routines uit 1997. Een op tekst gebaseerde AI migreert alles wat hem wordt gegeven — hij kan geen onderscheid maken tussen actieve code en dode code.
Onze call graph identificeert onbereikbare nodes — paragrafen of bestanden zonder inkomende edges, wat betekent dat niets ze aanroept. We markeren deze dode code voor verwijdering vóór de migratie begint. In onze ervaring reduceert dit de codebase doorgaans met 20-30%. Dat is geen kleine optimalisatie. Dat is het elimineren van een kwart van het werk en een kwart van het aanvalsoppervlak.
"Lossen grotere contextvensters dit niet op?"
Ik krijg deze vraag nog steeds voortdurend. De aanname is dat als GPT-5 of Claude 4 tien miljoen tokens aankan, het "Lost in the Middle"-probleem verdwijnt.
Dat gebeurt niet. En dit is waarom.
Zelfs als de attention-degradatie verbetert — en dat zal het — doe je nog steeds tekstretrieval. Je zoekt nog steeds naar vergelijkbare strings in plaats van logische verbindingen te doorlopen. Een contextvenster van een miljoen tokens helpt niet als de variabele die je nodig hebt, is gedefinieerd in een bestand dat nul trefwoorden deelt met het bestand dat je vertaalt. Het probleem is niet de grootte van het venster. Het probleem is dat het venster naar het verkeerde ding kijkt.
Het andere bezwaar dat ik hoor: "Kennisgrafieken zijn duur om te bouwen." Dat zijn ze. Een hele repository parsen, symbolen resolven, transitieve afsluiting berekenen — het is een aanzienlijke investering vooraf. Maar overweeg het alternatief. Handmatige migratie van een groot COBOL-systeem kost tientallen miljoenen dollars en duurt jaren. Op wrappers gebaseerde AI-migratie kost minder vooraf maar genereert een gestage stroom door hallucinatie veroorzaakte bugs die dure menselijke debugging vereisen. De grafiekgebaseerde aanpak heeft hogere opstartkosten en dramatisch lagere herwerkkosten. Gegevens van McKinsey suggereren dat GenAI codeertaken met 50% kan reduceren, maar alleen als het correct wordt ingezet. We hebben 2x tot 3x productiviteitsverbeteringen van ontwikkelaars gezien vergeleken met standaard AI-tools, specifiek omdat ontwikkelaars stoppen met urenlang zoeken naar waar een variabele is gedefinieerd.
De kaart is de asset
Dit is wat ik wou dat ik in het begin had begrepen: de kennisgrafiek is niet zomaar een tool voor migratie. Het is een permanente asset.
Zodra je codebase als een grafiek bestaat, blijft het een levende representatie van je systeem. Naarmate de nieuwe Java-code evolueert, werkt de grafiek bij. Je krijgt geautomatiseerde documentatie die altijd actueel is. Je krijgt detectie van architecturale drift — het systeem waarschuwt je als nieuwe code de modulariteitsregels schendt die je hebt gedefinieerd. Je krijgt impactanalyse op aanvraag: "Als ik deze methode wijzig, wat breekt er dan?"
Modernisatie is geen eenmalige gebeurtenis. Het is een levenscyclus. De organisaties die het als een project behandelen — met een startdatum en een einddatum — zijn degene die over vijf jaar weer eindigen waar ze begonnen, verdrinkend in een nieuwe generatie technische schuld.
Code is geen tekst
De les waar ik telkens op terugkom — die van die dinsdagavond starend naar een stacktrace — is bedrieglijk eenvoudig: code is geen tekst, en tools die het als tekst behandelen zullen resultaten produceren die er goed uitzien en verkeerd functioneren.
De hele "AI-wrapper"-economie is gebouwd op een categoriefout. Ze gaat ervan uit dat omdat LLM's buitengewoon zijn in het verwerken van taal, ze buitengewoon moeten zijn in het verwerken van code. Maar code is geen taal. Code is een grafiek — een dicht, onderling verbonden systeem van afhankelijkheden, datastromen en toestandsveranderingen dat gelijktijdig in meerdere dimensies bestaat. Proberen het te moderniseren met op tekst gebaseerde tools is als navigeren door een stad met een lijst van straatnamen maar geen kaart. Je raakt "lost in the middle."
Wij bouwden de kaart. En het werkt — niet omdat we slimmer zijn dan de teams die foundation models bouwen, maar omdat we een andere vraag stelden. Zij vroegen: "Hoe zorgen we dat AI tekst beter begrijpt?" Wij vroegen: "Wat als het probleem helemaal geen tekst is?"
De toekomst van legacymodernisatie is geen groter taalmodel. Het is een systeem dat software begrijpt op de manier waarop software werkelijk werkt — als structuur, niet als strings.
Dat is de gok die we bij Veriprajna hebben genomen. Elke dag ontdekt een andere organisatie dat hun door AI gegenereerde Java prachtig compileert en catastrofaal faalt. Elke dag wordt de kloof tussen syntactische vertaling en semantisch begrip duurder om te negeren. De organisaties die die kloof dichten, zullen niet alleen hun code moderniseren. Ze zullen het eindelijk begrijpen — velen van hen voor het eerst.