Ask someone to modernize a COBOL program and they picture a syntax problem — old keywords in, new keywords out. The real risk lives somewhere quieter: how the old system does math.
Take a field declared PIC 9(7)V99 COMP-3. That COMP-3 is packed decimal — a compact, exact, base-10 representation the mainframe uses precisely because money can’t tolerate the rounding errors of binary floating point. Translate that field naively into a double and you haven’t just changed a type. You’ve changed the answer. Fractions of a cent, compounded across millions of transactions, become real money — and a “successful” migration that quietly corrupts balances is worse than one that fails loudly.
This is why modernization can’t be a find-and-replace. The behavior has to be preserved, not just the shape.
What “getting it right” looks like
An agent modernizing that field has to do three things, in order:
- Recognize the intent.
COMP-3signals fixed-point exactness — map it tojava.math.BigDecimal, not a float. - Recover the business rule. The surrounding
IF WS-BALANCE < 0 MOVE ZERO TO WS-INTERESTisn’t noise; it’s a policy — no interest on an overdrawn account — that has to survive the rewrite. - Lock the behavior with a test. Before you trust the new code, synthesize a characterization test from the old one’s observable behavior, so any drift shows up as a failing test rather than a support ticket.
Do that, and the diff you hand a human reviewer comes with a claim you can actually check: zero behavioral differences. That’s the bar. Anything less isn’t modernization — it’s a gamble with someone else’s ledger.
Part of an ongoing series on making legacy systems tractable to modernize. Feedback welcome — get in touch.
