Thought - Why Banks Can't "Just Rewrite" COBOL in Java/Python/Go?
This is technical debt. We are going to rewrite the Core Ledger in Java microservices. We will be cloud-native
It happens every five years. A new CTO arrives at a Global 500 bank.
They look at the “Green Screen” terminals, the 40-year-old IBM Z/OS mainframes, and the millions of lines of COBOL.
They cringe.
“This is legacy,” they declare.
“This is technical debt. We are going to rewrite the Core Ledger in Java microservices. We will be cloud-native.”
Three years and $50 million later, the project is quietly cancelled.
The CTO moves on to a different company.
The COBOL remains.
Why?
It’s not because the bank is lazy. It’s not because we lost the source code.
It is because the fundamental architecture of modern programming languages is hostile to the requirements of global finance.
You are not fighting code, you are fighting how computers do math.
The Math Problem (IEEE 754)
When you write float or double in Java, Python, C++, or Go, you are using IEEE 754 Binary Floating Point arithmetic.
This standard is designed for scientific calculation, measuring the distance to a star or the velocity of a particle.
It prioritizes range and speed over absolute precision.
The Rounding Error
Open your browser console or a Node.js shell and type
0.1 + 0.2
// The result: 0.30000000000000004In a physics simulation, that 0.00000000000000004 is noise.
In a banking ledger processing trillions of dollars a day, that error is a regulatory violation, a failed audit, and potentially a lawsuit.
Fixed-Point Decimal (COBOL Way)
COBOL was not designed for science.
It was designed for Business (Common Business Oriented Language).
It does not use Binary Floating Point for money. It uses Fixed-Point arithmetic stored as Binary-Coded Decimal (BCD).
In COBOL, you define a variable like this
01 ACCOUNT-BALANCE PIC S9(13)V99 COMP-3.01: Level Number. In COBOL,01represents a top-level record or variable. Think of it likeconstorletat the root scope.ACCOUNT-BALANCE: The Variable Name. (COBOL uses kebab-case because it was invented before snake_case or camelCase won the war).PIC: Picture Clause. This tells the compiler exactly what the data looks like.S: Signed. This bit tracks if the number is positive or negative.9(13): Numeric Integers. The number9represents a digit.(13)means “allocate space for 13 of them.”V: Virtual Decimal. This is the magic. It tells the CPU “assume a decimal point here,” but it does not store a dot character in memory. It saves space.99: Precision. Allocate exactly 2 digits for cents.COMP-3: Packed Decimal (BCD). This is the instruction to store 2 digits per byte (using 4 bits each), rather than the standard 1 byte per character. This is what enables the hardware math precision.
When COBOL calculates 0.1 + 0.2, it does not convert them to binary approximations.
It calculates them in base-10, digit by digit, often utilizing specific hardware instructions on the mainframe (Decimal Floating Point units) that x86 architectures have historically lacked or emulated poorly.
The result is exactly 0.3. Always.
The Cost of Emulation
You must be wondering can’t we do this in Java?
Yes, using java.math.BigDecimal.
But BigDecimal is a software object. It adds memory overhead and CPU cycles for every single calculation.
COBOL operates on money at the instruction-set level. When you are processing 50,000 transactions per second (TPS), that overhead isn’t just a performance hit.
It’s an infrastructure bill.
True Transactionality (The CICS)
The second reason rewrites fail is the misunderstanding of “Transactionality.”
Modern web development is obsessed with “Statelessness.”
You send a REST request, the server forgets you, and you send another.
State is hard.
The Mainframe world runs on CICS (Customer Information Control System). CICS is an application server that manages transactions with ACID properties (Atomicity, Consistency, Isolation, Durability) as a law of physics.
Lets take an example of ATM withdrawal
Check Balance.
Debit Ledger.
Dispense Cash.
Log Audit Trail.
In CICS, if step 3 fails (the cash jams), the entire transaction rolls back instantly. The ledger is never touched.
The state remains consistent.
The Microservices Nightmare
In a distributed microservices architecture, you break these steps into different services.
Service A (Ledger) debits the account.
Service B (ATM) fails to dispense.
Now Service A must “compensate” (undo) the transaction.
You have moved from immediate consistency to Eventual Consistency.
You are now managing “Distributed Transactions” and “Sagas.”
So question to you is, do you want your checking account balance to be “Eventually Consistent”?
Or do you want it to be Correct?
Scale Up vs. Scale Out
The philosophy of the modern cloud is Scale Out.
“If the server is overloaded, spin up 100 more cheap nodes. If a node fails, kill it and retry.”
The philosophy of the Mainframe is Scale Up.
“This single machine will process everything. It will not fail. If a CPU dies, a backup CPU takes over without the operating system even noticing.”
Mainframes utilize specialized I/O Processors (Channel Subsystems).
The main CPU doesn’t waste time reading from a disk or talking to the network card. It delegates that to a sub-processor and keeps crunching numbers. This allows mainframes to run at 100% CPU utilization for years without throttling.
Try running a Linux server at 100% CPU for an hour, it will become unresponsive.
Finally, the verdict is
For Twitter, Scale Out. If a tweet fails to load, nobody loses money.
For the Global Economy, Scale Up. Reliability is not optional.
Why this matters to You?
Even if you never touch a line of COBOL, the principles of the Mainframe apply to your modern stack.
Never Use Floats for Money
If you are building a FinTech app in JavaScript or Python, do not use standard number types for currency.
const price = 19.99; // Is Wrong
Use libraries like decimal.js, Python’s decimal module, or store values as Integers (cents) and format them only for display
(const priceInCents = 1999;)
Respect the Monolith
We have been trained to think “Microservices = Good” and “Monolith = Bad.”
But if your application requires strict ACID compliance (inventory management, voting systems, banking), a well-structured Monolith with a single database transaction is infinitely less buggy than a distributed system trying to coordinate state across ten services.
“Legacy” is a Success Metric
Code becomes “legacy” only if it survives.
If you are looking at a system that has been running for 30 years, do not mock it.
Learn from it. It has survived market crashes, leap years, and user stupidity for decades.
It is doing something right.
Conclusion
Rewriting a Core Banking System is rarely a “refactoring” effort. It is an archaeological excavation.
That “ugly” COBOL code contains 40 years of edge cases, the tax law change of 1992, the negative interest rate handling of 2015, the specific rounding rule required by the Bank of Japan.
If you rewrite it, you will miss these rules.
You will introduce bugs that were solved in 1985.
Don’t replace the Mainframe. Modernize it.
Wrap the COBOL logic in REST APIs (using Z/OS Connect).
Let the Java frontend look pretty, but let the Iron down in the basement handle the math.
Respect the Old Gods.
They are still holding up the sky.


