<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Tho Nguyen</title>
    <description>The latest articles on DEV Community by Tho Nguyen (@tho_nguyen).</description>
    <link>https://dev.to/tho_nguyen</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4231835%2Fcbd8f17a-6e8d-4aaa-8122-b822ff250c40.jpg</url>
      <title>DEV Community: Tho Nguyen</title>
      <link>https://dev.to/tho_nguyen</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tho_nguyen"/>
    <language>en</language>
    <item>
      <title>Resurrecting COBOL with receipts: how we built COBOL Phoenix</title>
      <dc:creator>Tho Nguyen</dc:creator>
      <pubDate>Sat, 07 Nov 2026 09:52:57 +0000</pubDate>
      <link>https://dev.to/tho_nguyen/resurrecting-cobol-with-receipts-how-we-built-cobol-phoenix-2m3j</link>
      <guid>https://dev.to/tho_nguyen/resurrecting-cobol-with-receipts-how-we-built-cobol-phoenix-2m3j</guid>
      <description>&lt;p&gt;&lt;strong&gt;This post is written as part of the Kiroween 2026 hackathon.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every legacy-modernization pitch promises to “convert your COBOL”. Converting is not the problem. The problem is the&lt;br&gt;
question every architect asks afterwards: &lt;strong&gt;how do we know the new code behaves exactly like the old one?&lt;/strong&gt;&lt;br&gt;
COBOL Phoenix is our answer: understand the program deterministically, generate code you can trace line by line,&lt;br&gt;
and prove behaviour by running the original and the generated code side by side.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcb1gngbvyqg705mzs97x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcb1gngbvyqg705mzs97x.png" alt=" " width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Rule 1: no guessing in the core
&lt;/h2&gt;

&lt;p&gt;We wrote a real COBOL front end in Python — a fixed/free-format lexer, a COPY … REPLACING preprocessor and a&lt;br&gt;
recursive-descent parser — producing an intermediate representation where every node carries a file and line&lt;br&gt;
range. Business rules, the dependency graph and the generated code are all derived from that IR. Anything outside&lt;br&gt;
the supported subset (file I/O, INSPECT, ALTER, EXEC SQL…) is reported with its location and becomes an explicit&lt;br&gt;
&lt;code&gt;UnsupportedConstruct&lt;/code&gt; in the generated code. Nothing is silently dropped.&lt;/p&gt;
&lt;h2&gt;
  
  
  Rule 2: COBOL data is bytes
&lt;/h2&gt;

&lt;p&gt;The generated Python runs on a small runtime where each record is a &lt;code&gt;bytearray&lt;/code&gt; and each data item a typed view.&lt;br&gt;
That single decision makes REDEFINES, group MOVEs and CALL BY REFERENCE behave like COBOL instead of like Python&lt;br&gt;
dictionaries. Numbers are &lt;code&gt;Decimal&lt;/code&gt;, with COBOL's truncation, ROUNDED (half away from zero), sign overpunch and&lt;br&gt;
numeric-editing rules — each confirmed against GnuCOBOL with tiny probe programs.&lt;/p&gt;
&lt;h2&gt;
  
  
  Rule 3: prove it
&lt;/h2&gt;

&lt;p&gt;For a callable program we generate a COBOL harness that ACCEPTs each USING parameter, CALLs the program and&lt;br&gt;
DISPLAYs the results; the generated Python CLI speaks the same protocol. Identical bytes go into GnuCOBOL (inside a&lt;br&gt;
container with no network, a read-only filesystem and resource limits) and into Python. Outputs are decoded with the&lt;br&gt;
record layout and compared field by field.&lt;/p&gt;

&lt;p&gt;Test inputs come from fixtures plus values derived from the code itself: every comparison against a literal yields&lt;br&gt;
boundary values, every 88-level and EVALUATE WHEN yields matching and non-matching values. Then we got smarter:&lt;br&gt;
we trace the fixtures through the generated Python and attach each boundary value to a fixture that actually&lt;br&gt;
executes the statement it came from — &lt;em&gt;coverage-guided placement&lt;/em&gt;. On a public insurance repository that raised the&lt;br&gt;
mutation score from 67% to over 80% without a single hand-written test.&lt;/p&gt;
&lt;h2&gt;
  
  
  The best bug was our own
&lt;/h2&gt;

&lt;p&gt;An edge case with the maximum vehicle value failed. GnuCOBOL printed &lt;code&gt;$04,059.41&lt;/code&gt;; our runtime printed&lt;br&gt;
&lt;code&gt;$4,059.41&lt;/code&gt;. When significant high-order digits are truncated away, GnuCOBOL considers zero suppression already&lt;br&gt;
finished. We would never have found that by reading documentation. Differential testing found it in seconds.&lt;/p&gt;
&lt;h2&gt;
  
  
  Trust, measured
&lt;/h2&gt;

&lt;p&gt;Passing cases are evidence, not proof, so we measure the tests themselves: mutation analysis generates single-fault&lt;br&gt;
variants (drop a ROUNDED, flip a &lt;code&gt;&amp;lt;&lt;/code&gt; to &lt;code&gt;&amp;lt;=&lt;/code&gt;) and checks the suite catches them. In the UI you can inject one on&lt;br&gt;
purpose — remove one ROUNDED and watch eight cases fail by exactly one cent. On the insurance repository the&lt;br&gt;
surviving mutants pointed at a business rule that can never fire, because every plan caps the issue age below the&lt;br&gt;
rule's threshold. That is the kind of finding a modernization team actually needs.&lt;/p&gt;
&lt;h2&gt;
  
  
  Spec-first with Kiro
&lt;/h2&gt;

&lt;p&gt;We started from an empty repository by writing Kiro steering documents (product principles, COBOL semantics,&lt;br&gt;
security, testing), four specs with EARS requirements, designs and task lists, and hooks. The steering file on&lt;br&gt;
COBOL semantics turned out to be the most valuable artifact: it was the checklist we tested the runtime against.&lt;/p&gt;

&lt;p&gt;We then used Kiro IDE to execute spec tasks: support for &lt;code&gt;INSPECT … TALLYING / REPLACING&lt;/code&gt;, and OpenAPI request&lt;br&gt;
examples generated from fixtures. Because a &lt;code&gt;PostTaskExec&lt;/code&gt; hook runs the whole suite — including the differential&lt;br&gt;
tests against GnuCOBOL — every Kiro task had to keep the generated Python behaviourally identical to the original.&lt;br&gt;
Kiro even refused to guess GnuCOBOL's sign handling for INSPECT on signed numbers and reported those as&lt;br&gt;
unsupported instead, exactly as our steering asked. &lt;em&gt;(Add your own impressions of working with Kiro here.)&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Original sample: 127/127 cases identical to GnuCOBOL; injected fault caught (8 failing cases).&lt;/li&gt;
&lt;li&gt;Independent insurance repository: 180/180 on the underwriting program; generated drivers reproduce upstream golden files.&lt;/li&gt;
&lt;li&gt;Two more repositories: 20 programs parsed with zero errors; 365 unsupported constructs reported, not ignored.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;Open &lt;a href="https://cobol.beauty" rel="noopener noreferrer"&gt;cobol.beauty&lt;/a&gt;, click &lt;strong&gt;Import sample → Generate → Verify&lt;/strong&gt;, then inject a fault and watch&lt;br&gt;
it get caught. Or run it yourself with Docker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/thosuperman/cobol-phoenix &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;cobol-phoenix
docker compose up &lt;span class="nt"&gt;--build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;COBOL Phoenix is open source under Apache-2.0:&lt;br&gt;
&lt;a href="https://github.com/thosuperman/cobol-phoenix" rel="noopener noreferrer"&gt;github.com/thosuperman/cobol-phoenix&lt;/a&gt;.&lt;br&gt;
Preserve the logic. Rebuild the future.&lt;/p&gt;

&lt;h1&gt;
  
  
  KiroweenHackathon26 #Kiro #COBOL #Python
&lt;/h1&gt;

</description>
      <category>kiro</category>
      <category>cobol</category>
      <category>python</category>
      <category>kiroweenhackathon26</category>
    </item>
  </channel>
</rss>
