
A major bank fed thirty years of COBOL into an AI coding assistant and asked it to produce Java. The code compiled. The unit tests passed. And the first transaction in testing crashed the database.
The AI had translated the syntax perfectly — every semicolon, every method signature, every variable name. But it missed a single dependency defined in a shared file thousands of lines away from the code it was translating. That one blind spot caused the new Java application to write corrupted data into the database, triggering a cascade of failures. The code was flawless. The understanding was zero.
This isn't a one-off horror story. Between 70% and 80% of legacy modernization projects fail to meet their objectives. And the new wave of AI-powered migration tools — the ones promising to "just convert your COBOL" — are making the problem worse in ways most executives don't yet understand.
I've spent the last two years building a system that approaches this differently. Not by making AI faster at translating code, but by giving it something it fundamentally lacks: the ability to see how an entire codebase connects.
The $1.52 Trillion Trap Nobody Talks About
Roughly 43% of banking systems still run on COBOL. Those systems process 95% of all ATM transactions. The U.S. alone carries an estimated $1.52 trillion in technical debt from aging software, and 80% of federal IT budgets go to just keeping legacy systems alive — not improving them.
The people who wrote these systems are retiring. The knowledge of why a particular variable exists, why a specific calculation rounds down instead of up, why two modules communicate through a shared memory block rather than an API — that knowledge is walking out the door.
Every year you delay modernization, you're not standing still. You're falling behind while the cost of catching up compounds.
Organizations face a brutal choice. Option one: "lift and shift" the old code to the cloud, which changes your hosting bill but preserves every structural problem. Option two: manually rewrite everything, which costs a fortune and takes years. AI was supposed to be the third way — automated, fast, cheap. But the bank scenario I described isn't the exception. It's the pattern.
What Actually Went Wrong at the Bank

The failure mechanism is specific, and once you understand it, you'll see it everywhere.
COBOL programs use something called a COPYBOOK — think of it as a shared reference file that defines what variables mean and how they're structured. The bank's transaction processing code referenced a variable called TRN-LIMIT. That variable wasn't defined in the file the AI was translating. It was defined in a COPYBOOK included thousands of lines earlier in the execution chain.
Worse, that COPYBOOK contained a REDEFINES clause — a COBOL feature that lets the same piece of memory be interpreted as two completely different data types depending on a flag set in a different module entirely. The original system treated TRN-LIMIT as a packed decimal (a specific way mainframes store numbers). The AI, never having seen the COPYBOOK, assumed it was a standard integer.
Packed decimal and integer look identical in code. They behave completely differently in memory. The mismatch meant the Java application wrote binary garbage where the database expected clean financial data.
The AI didn't make a coding error. It made a seeing error. It couldn't see the dependency because the dependency existed outside its field of vision.
The "Lost in the Middle" Problem Is Worse Than You Think
This visibility failure has a name in AI research: the "Lost in the Middle" effect. Even though modern AI models can technically accept enormous amounts of text — up to a million tokens — they don't process all of it equally. Research shows a U-shaped performance curve: models pay strong attention to the beginning and end of their input, but their accuracy drops significantly for information buried in the middle.
In a modernization project, a single COBOL program can be thousands of lines long. Its dependencies can span dozens of files. When the definition of a critical variable lands in the middle of that massive context, the AI is statistically likely to overlook it.
And when it overlooks something, it doesn't stop and ask. It guesses. In AI terminology, it "hallucinates" — invents a plausible-sounding definition based on probability. In a banking system, a plausible guess about a variable type can corrupt every transaction that touches it.
AI coding assistants don't fail by writing bad code. They fail by writing confident code about things they never actually saw.
I explored this failure pattern in depth in our interactive analysis, which walks through the specific mechanisms that make standard AI approaches structurally unsuited for enterprise-scale code migration.
Code Is Not Text. It's a Map.
This is the insight that changed how my team approaches the problem.
When you ask an AI to translate a novel from French to English, each sentence carries most of its meaning independently. Context helps, but a sentence on page 200 rarely depends on a specific word choice on page 3.
Code is the opposite. A single line — x = y + 1 — means absolutely nothing unless you know where y was defined, what type it is, what other functions modify it, and what state it's in when this line executes. That definition might live in a different file, a different module, or be inherited from a parent structure. Code isn't a document. It's a network of dependencies.
Standard AI tools treat code like text — they chop it into segments, search for segments that look relevant based on keyword similarity, and feed those segments to the model. This is called Vector RAG (Retrieval-Augmented Generation), and it works well for searching FAQs or documentation.
It fails catastrophically for code. If you search for "payment logic," you'll find files that mention payments. You won't find the file called GlobalVarDef.cbl that defines the tax rate used by the payment logic — because that file never uses the word "payment." The connection between them is structural, not textual.
We Built a Map Instead of a Better Search Engine
The core of what we built at Veriprajna is a Repository-Aware Knowledge Graph — a complete structural map of every entity in a codebase and every relationship between them.
Instead of treating code as text to be searched, we parse it into its actual logical structure. Every variable, every function, every database table, every file dependency becomes a node in a graph. Every relationship — this function calls that function, this variable is defined in that file, this module updates that database table — becomes an edge connecting those nodes.
The difference is fundamental. When a standard AI tool encounters the bank's TRN-LIMIT variable, it searches for text that looks similar. When our system encounters it, it traverses the graph: TRN-LIMIT → defined in COPYBOOK-X → COPYBOOK-X contains a REDEFINES clause → the REDEFINES depends on a flag in MODULE-Y. Every link in that chain is a hard fact, not a statistical guess.
We call this process transitive closure — following dependencies not just one step, but all the way to their root. If A depends on B and B depends on C, the AI needs to see C when working on A. Standard tools see A and maybe B. Our graph guarantees the AI sees the complete chain.
The question isn't "Can AI translate COBOL to Java?" It can. The question is "Can AI see everything it needs to see before it starts translating?" That's the problem we solved.
GraphRAG: Retrieval That Follows Logic, Not Keywords

We replaced standard vector search with what's called GraphRAG — Graph Retrieval-Augmented Generation. When someone asks the system to refactor the payment logic, the process works in three stages.
First, vector search finds the entry point — the function or paragraph most likely to contain payment logic. That part is the same as standard tools.
Then the system does something standard tools can't: it walks the graph outward from that entry point. It follows CALLS edges to find every subroutine involved. It follows READS edges to find every variable definition. It follows INCLUDES edges to find every shared file. These connected pieces might share zero keywords with "payment logic," but they're logically inseparable from it.
Finally, all of these connected pieces are assembled into a coherent context for the AI. The model doesn't receive a random collection of text snippets that looked relevant. It receives a self-contained slice of the application's actual logic — complete with every dependency resolved.
In testing, this approach dramatically outperforms standard retrieval on what researchers call "multi-hop reasoning" — questions where the answer requires connecting facts separated by several steps. "If I change the interest rate calculation in Module A, which reporting screens in Module Z will break?" Vector search can't answer that because Modules A and Z share no common keywords. The graph traces the chain of function calls connecting them and gives a definitive answer.
For the full technical methodology behind our graph construction and retrieval pipeline, see our detailed research.
What This Actually Looks Like in Practice
The practical impact shows up in three places that consistently surprise the teams we work with.
The global variable trap disappears. COBOL programs rely heavily on global variables — values that any part of the program can read or modify. Modern Java best practice demands the opposite: every function should explicitly declare what it needs. Our system traces the lifecycle of every variable through the graph, identifies which functions read it and which modify it, and automatically converts implicit global dependencies into explicit method parameters. This is exactly the refactoring that would have prevented the bank's crash.
GOTO spaghetti gets untangled. COBOL's GOTO statement lets program execution jump anywhere, creating tangled control flows that have no equivalent in Java. A text-based AI typically converts these into recursive function calls that crash with stack overflow errors, or simply invents a logic flow that doesn't match the original. Our system maps every GOTO destination in a control flow graph, recognizes patterns — this backward jump is actually a loop, this forward skip is actually an if/else — and generates clean, structured Java.
Dead code gets cut before migration starts. Legacy systems accumulate decades of unused code: old promotions, retired products, debug routines. Our call graph identifies every function with no callers — code that literally nothing in the system ever executes. Flagging this for removal before migration typically reduces the codebase by 20-30%, saving significant time and money while shrinking the security attack surface.
"But What About the AI's Judgment Calls?"
The most common pushback I hear from engineering leaders is about trust. If the AI is making decisions about how to restructure code, how do you audit those decisions?
This is where the graph provides something a neural network alone never can: a citation chain. Every decision the AI makes is traceable back through the graph. "The AI imported this library because it found a dependency on COPYBOOK-X, which defines variable Y as a packed decimal, which requires BigDecimal in Java." A human reviewer can follow that chain and verify it in minutes.
In regulated industries — banking, healthcare, government — this isn't a nice-to-have. It's a requirement. You can't deploy code you can't explain to an auditor.
The other question I get is about scale. "Does this work on a codebase with millions of lines?" It does, precisely because graph traversal scales in ways that stuffing text into AI context windows does not. The graph doesn't need to fit in a context window. It lives in a graph database, and the AI queries it on demand — pulling exactly the subgraph it needs for each specific translation task.
The Difference Between Translating and Understanding
McKinsey estimates that generative AI can reduce coding tasks by 50% — but only if deployed correctly. The "if" in that sentence is doing enormous work.
The current generation of AI coding assistants are translators. They convert syntax from one language to another. What enterprise modernization actually requires is understanding — grasping how millions of lines of code connect, depend on each other, and collectively implement business logic that took decades to evolve.
My team didn't set out to build a better translator. We set out to build the understanding layer that makes translation safe. The Knowledge Graph is that layer. It's the difference between handing someone a list of street names and handing them an actual map of the city.
Modernization isn't a one-time event, either. Once a codebase is mapped into a Knowledge Graph, that graph becomes a living asset. As the new code evolves, the graph updates. It generates documentation automatically. It detects when new code violates the architectural rules that were so painfully established during migration.
The bank that crashed its database didn't have a coding problem. It had a seeing problem. The AI couldn't see the dependency that mattered most, so it guessed — and the guess was catastrophic.
If you're evaluating AI tools for a modernization initiative, the first question to ask isn't "How fast can it translate?" It's "How does it resolve dependencies it can't see in the current file?" If the answer involves context windows and token limits, you're looking at a translator. And translators break banks.
I'd genuinely like to hear from anyone navigating this — what's your experience been with AI-assisted migration? Where has it worked, and where has it failed in ways you didn't expect?