COBOL to C# / .NET & Python Migration

ModernLift · ·14 min read
Part 6 of 10

C# on .NET and Python are the two most common non-Java targets for COBOL migration. C# is the natural choice for organizations standardized on Microsoft tooling; like Java it is a mature, statically typed enterprise language, and COBOL packed-decimal fields map cleanly to its native decimal type. Python fits where the destination is data, analytics, or scripting work rather than high-volume transaction processing — but for ledger-grade workloads it is usually the wrong default, because it is dynamically typed, slower at heavy numeric batch, and requires the decimal module rather than a native decimal type. The data-fidelity rules are identical across all targets.

Part 5 treated Java as the default COBOL target and explained why that default is sound. This part covers the two most common alternatives — C# on .NET and Python — because “default” is not “always,” and choosing the wrong target for the wrong reason is an expensive mistake to discover late. The encouraging news is that the hard part does not change between targets: the data-fidelity rules from Part 4 are identical everywhere. What changes is fit.

C# / .NET: the Microsoft-shop default

For an organization already standardized on Microsoft tooling, C# on .NET is at least as natural a target as Java, and often the better one. The reasoning mirrors the Java case, pointed at a different ecosystem:

  • It minimizes new ecosystems. A shop running Windows infrastructure, Azure, SQL Server, and existing .NET teams gains little by introducing the JVM alongside all of that. Migrating COBOL to C# keeps the destination inside an ecosystem the organization already operates, hires for, and trusts. The fewer net-new platforms a migration introduces, the lower its operational risk.
  • It is a mature, statically typed enterprise language. Modern .NET (the unified, cross-platform successor to the old .NET Framework) is fast, well-supported, runs in containers and in the cloud, and has the compile-time type safety that helps catch mapping errors across a large migration — the same safety-net advantage Java offers.
  • Decimal maps cleanly. C# has a native decimal type for exact base-ten arithmetic. COBOL COMP-3 and zoned-decimal fields map onto it directly, the same way they map onto Java’s BigDecimal — with the same obligation to match scale and rounding per field, and the same prohibition on using a binary double.

In capability terms, C# and Java are near-peers for ledger work. The decision between them is rarely technical; it is about which ecosystem the organization already lives in. If that is Microsoft, C# is the lower-friction path, and choosing Java instead would mean taking on a new platform for no offsetting benefit.

Python: right for some workloads, wrong as a default

Python comes up often in COBOL migration conversations, usually for the wrong reason — its popularity — and sometimes for very good ones. The honest position is that Python is an excellent target for some COBOL and a poor default for most core transactional COBOL.

Where Python genuinely fits:

  • Data transformation and analytics. COBOL that exists to extract, reshape, and report on data is often better in Python than in Java or C#, because Python’s data ecosystem is unmatched and the code is more readable.
  • Reporting and glue logic. Batch steps that orchestrate, format, and move data — rather than performing high-volume transactional arithmetic — translate comfortably and benefit from Python’s expressiveness.
  • Smaller, lower-throughput workloads where developer productivity matters more than raw batch performance.

Where Python is the wrong default:

  • Dynamic typing removes a safety net. A large COBOL migration is, in part, a massive type-mapping exercise. Static typing in Java and C# catches a whole class of mapping errors at compile time; Python defers them to runtime, which in a money system means deferring them to production.
  • Numeric batch performance. For the heavy, high-volume overnight batch that is COBOL’s home turf, Python is meaningfully slower than the JVM or .NET. That can matter against a fixed batch window.
  • Decimal is opt-in, not native. Python can do exact decimal arithmetic — via the decimal.Decimal type — but its default float is binary floating-point. A developer who reaches for the obvious float introduces exactly the money-rounding bug that double causes in Java. The exactness is available; it is not the default, which makes it easier to get wrong.

The summary: choose Python where the workload is data-shaped and the team is Python-shaped. Do not choose it as a blanket replacement for ledger code because it is the language everyone knows. Matching the target to the workload beats matching it to fashion.

The constant across every target

Step back and the most important point of this part is what does not change between Java, C#, and Python:

  • Packed decimal maps to an exact decimal type — never a binary float. BigDecimal in Java, decimal in C#, decimal.Decimal in Python. Scale and rounding matched per field, every time. This rule is target-independent and non-negotiable.
  • PICTURE semantics, REDEFINES, OCCURS DEPENDING ON, and EBCDIC are migration problems regardless of destination. No target language makes them disappear; they are properties of the COBOL data, not of the language you are moving to.
  • The undocumented business rules must be reproduced exactly, and the only way to know they were is parity validation against the running system. The target language has no bearing on whether you skip that gate — and you cannot.

This is why Part 4 insisted that the target-language debate is a detail. It is a real decision with real fit consequences, but it sits on top of a data-fidelity problem that is the same in every language. Teams that argue for months about Java versus C# while underinvesting in decimal mapping and rule recovery are optimizing the easy variable and neglecting the hard one.

A note on mixed-target estates

Real migrations are not monolingual. A large estate might convert its transactional core to Java or C# for safety and performance, while re-implementing its reporting and data-extraction COBOL in Python because that is where Python shines. That is not indiscipline; it is matching each workload to the target that fits it, within a single coherent program. The constraint is that the integration between those pieces must be designed deliberately, and every piece — whatever its language — passes the same parity gate before cutover.

Where this leads

Across all three targets, the same phrase keeps recurring: the undocumented business rules must be reproduced exactly. That is the heart of COBOL migration, and it deserves its own part. Part 7, Preserving Business Logic in a COBOL Migration, is about the rules buried in decades of code that were never written down — how they are recovered from the COBOL before the experts retire, captured as living specifications, and carried into the target language without being silently lost.

Frequently asked questions

Is C# or Java better for COBOL migration?
Neither is universally better; the deciding factor is the organization's existing platform and skills. C# on .NET is the stronger fit for shops already standardized on Microsoft — Windows infrastructure, Azure, existing .NET teams — because it minimizes the number of new ecosystems to learn and operate. Java tends to win where the enterprise is already JVM-centric or wants the largest possible hiring pool. Both are mature, statically typed, and have native exact-decimal support, so technically either can carry a ledger safely; the choice is mostly about fit, not capability.
Can you migrate COBOL to Python?
Yes, and it is a good fit for some workloads — data transformation, analytics, reporting, and glue logic where Python's ecosystem and readability are real advantages. It is usually the wrong default for core transactional COBOL, though. Python is dynamically typed, which removes the compile-time safety net that helps catch mapping errors in a large migration; it is slower for heavy numeric batch; and exact decimal arithmetic requires the decimal module rather than a native type. Use Python where its strengths apply, not as a blanket replacement for ledger code.
Does COBOL decimal arithmetic map safely to C# and Python?
Yes, if you use the right types. C# has a native decimal type that provides exact base-ten arithmetic and maps cleanly from COBOL COMP-3 fields. Python provides the same exactness through its decimal.Decimal type, but you must use it deliberately — the default float type is binary floating-point and will introduce rounding errors in money, exactly as double does in Java. In every target language the rule is identical: map packed decimal to an exact decimal type with matched scale and rounding, never to a binary float.
All 10 parts of COBOL Modernization →