The first Java change request after a COBOL translation is a better test of the migration than the first successful compile. A developer opens BillingProcessor.java to fix a minor defect. The file is 14,000 lines long. There are no domain objects like Customer or Invoice. There is a single ExecutionContext object passed into every method, simulating COBOL’s global working storage. The methods are named para_100_init() and para_200_read_file(). The developer stares at the screen, closes the IDE, and asks for the original COBOL listing.

The steering committee saw a different picture. The vendor demo showed a command-line tool ingesting three million lines of procedural code. A progress bar zipped across the screen. The IDE populated with green checkmarks. The code compiled. The automated test suite passed. The project was marked green, and the mainframe hardware was decommissioned. The vendor reported 95% automated translation. They were technically correct. The remaining 5% was the actual business logic, and it was entirely unmaintainable.

You can translate syntax automatically. You cannot translate an architecture. Procedural languages assume global state and sequential execution. Object-oriented languages assume encapsulated state and polymorphic behavior. When a translation tool bridges this gap, it defaults to the lowest common denominator.

COBOL programs define variables in a single Data Division. Any paragraph can read or mutate any variable. The translator solves this by generating a single massive class containing every variable, which is then injected into every generated method. Encapsulation is bypassed entirely. Control flow flattens. PERFORM ranges and GO TO logic do not map cleanly to standard Java method invocations. The tool generates massive switch statements inside while(true) loops to manage the execution state machine. Data types require bridging. Packed decimals become BigDecimal wrappers where every mathematical operation requires method calls instead of simple operators. Memory overlays using REDEFINES become manual byte array manipulations.

The tool translated the syntax. The structure came along for the ride. You didn’t modernize the system. You changed its accent. The goal of the project was to stop needing COBOL programmers. The result is writing Java that only a COBOL programmer can read. You traded a scarce skill for a scarcer skill.

The structural mismatch eventually hits the runtime. Translated code preserves the I/O patterns of the source system. Those patterns do not belong on modern hardware. A batch job that took four hours on the mainframe takes eleven hours on cloud infrastructure. The infrastructure team vertically scales the JVM, but throughput barely changes. The translated code is performing synchronous, row-by-row file reads, emulating sequential tape access over a network file system. The performance regression is invisible until the first production run.

The vendor’s success metrics are not your success metrics. “Compiles” and “passes tests” are not “maintainable.” If the tool that translated the code also generated the test suite, you are testing the translation, not the behavior. The tests pass because they verify that the Java produces the exact same output file for a representative run. They do not measure the blast radius of a future code change. They do not test what happens if the job stops after writing half the file, or if an input record has an unexpected status code.

Line-by-line translation rarely relies on standard libraries. To make Java behave like a mainframe environment, the vendor provides a proprietary emulation framework. The generated code imports a proprietary mainframe data structure class in every file. You escaped the hardware licensing, but you are now locked into the vendor’s runtime framework.

Automated translation is a deployment strategy, not a development strategy. It moves the execution environment. It does not improve the software. This distinction matters when planning the next phase. Translating code to an unmaintainable state is a valid strategy if it is explicitly temporary. It gets you out of a closing data center. It stops the bleeding on mainframe processing costs. If the organization treats the translated Java as a black box to be strangled out via the Strangler Fig pattern over the next three years, the translation did its job.

It becomes a trap when the business case assumes that running on Java also means Java developers can change it at normal cost. If the plan is to build new capabilities on top of the translated monolith, development will stop. The first feature request after go-live is when the problem becomes visible. You cannot refactor your way out of a translation. You can only rewrite it.

The translation tool is a compiler with a sales team. Treat its output the way you would treat any compiler’s output: as an artifact, not a design. The mainframe is gone, but the legacy is still here, running on a more expensive machine.