Skip to content
PYD ENTERPRISES

Migration guide

COBOL to Go microservices: decimal rules, explicit errors and small services

Go suits small, explicit services, which is the opposite of a 9,000-line COBOL program. This guide shows how to cut the program into decisions and contracts first, and which COBOL semantics Go handles differently.

COBOLGo microservices
DIFFICULTY
High
OUTPUT
Rule / schema JSON
SYNTAX TRAPS
5 covered
WORKFLOW
6 steps

By Pranav Kulkarni, Head of Language Engineering · Updated 15 Oct 2026

Technical syntax challenges

Where COBOL migrations go wrong.

01

Go has no built-in decimal type

float64 cannot represent 0.10 exactly. COBOL fixed-point fields need a decimal library or scaled integers (cents as int64) with the scale taken from the PIC clause. The spec has to state scale and rounding per field so every service makes the same choice.

02

EVALUATE TRUE ordering

EVALUATE TRUE takes the first matching WHEN. Go's switch with no tag behaves the same, but teams that turn the table into a map lookup lose the ordering. The extracted decision uses hit policy FIRST to make the order explicit.

03

FILE STATUS and SQLCODE become errors

COBOL checks FILE STATUS values like 23 (record not found) and SQLCODE +100 after each call, often routing several codes to the same paragraph. Each distinct business outcome should become a typed Go error or result, and the spec lists which codes lead where.

04

Shared WORKING-STORAGE

COBOL programs assume one task owns WORKING-STORAGE for the whole run, so values from a previous record can leak into the next one when a field is not reset. In Go, handlers run concurrently and must not share that state; the spec flags fields that carry over between iterations.

05

GO TO and paragraph fall-through

GO TO 3900-EXIT inside a PERFORM range and GO TO ... DEPENDING ON jump tables are flattened by the control-flow graph into explicit branches, which map cleanly to early returns in Go.

Extraction workflow

6 steps from source to signed-off spec.

The sequence a PYD ENTERPRISES Discovery Scan runs for this stack. Every output links back to source file and line range.

  1. 1

    Parse the program family

    Ingest the COBOL programs, copybooks and called subprograms behind the business capability you want as services.

  2. 2

    Resolve control flow and carry-over state

    PYD ENTERPRISES builds the control-flow graph through GO TO, PERFORM THRU and EVALUATE, and flags WORKING-STORAGE fields read before being set in a loop iteration.

  3. 3

    Cluster rules into candidate services

    Rules are grouped by the data they read and write; clusters with few shared fields are proposed as service boundaries.

  4. 4

    Export decisions with explicit hit policies

    Each EVALUATE and nested IF cascade becomes a DMN 1.5 table with hit policy, scale and rounding recorded per output.

  5. 5

    Export record and API contracts

    Copybooks become JSON Schema and candidate operations become OpenAPI 3.1, so Go structs and handlers can be written or generated by your team from standard tooling.

  6. 6

    Generate fixtures and run them in Go tests

    Boundary fixtures are exported as JSON; Go teams load them in table-driven tests and run them in CI against each service.

Legacy in, specification out

The source, and what PYD ENTERPRISES extracts from it.

Left: the COBOL construct as it sits in production. Right: the reviewed artifact your Go microservices engineers build against. PYD ENTERPRISES produces the spec and the tests; your team writes the service.

LEGACY SOURCE

WIREFEE1.cblCOBOL
1 3000-WIRE-FEE.
2 EVALUATE TRUE
3 WHEN WR-INTL AND WR-AMOUNT > 50000
4 MOVE 45.00 TO WR-FEE
5 WHEN WR-INTL
6 MOVE 35.00 TO WR-FEE
7 WHEN WR-SAME-DAY
8 MOVE 25.00 TO WR-FEE
9 WHEN OTHER
10 MOVE 15.00 TO WR-FEE
11 END-EVALUATE
12 IF CUST-PREMIER
13 COMPUTE WR-FEE = WR-FEE * 0.5
14 END-IF.

EXTRACTED ARTIFACT

WIREFEE-01.fixtures.jsonJSON
1{
2 "decision": "WIREFEE-01 Wire transfer fee",
3 "hitPolicy": "FIRST",
4 "source": "WIREFEE1.cbl:412-426",
5 "fixtures": [
6 { "in": { "intl": true, "amount": "50000.00", "premier": false }, "out": { "fee": "35.00" }, "rule": "R2" },
7 { "in": { "intl": true, "amount": "50000.01", "premier": false }, "out": { "fee": "45.00" }, "rule": "R1" },
8 { "in": { "intl": true, "amount": "50000.01", "premier": true }, "out": { "fee": "22.50" }, "rule": "R1+P" },
9 { "in": { "intl": false, "sameDay": true, "premier": true }, "out": { "fee": "12.50" }, "rule": "R3+P" },
10 { "in": { "intl": false, "sameDay": false, "premier": false }, "out": { "fee": "15.00" }, "rule": "R4" }
11 ]
12}

Architectural best practices

Build the Go microservices side to the spec, not to the old shape.

  • Pick one decimal representation for the whole service estate and record it in the spec; mixing libraries reintroduces rounding drift.
  • Make every EVALUATE-derived decision a pure function with no I/O, so it can be tested with fixtures alone.
  • Map FILE STATUS and SQLCODE outcomes to a small set of typed errors with names from the business, not the codes.
  • Size services around rule clusters and the data they own, not one service per COBOL program.
  • Load the generated JSON fixtures in table-driven Go tests and fail the build on any mismatch.

From one program to several services

A typical online or batch COBOL program mixes validation, pricing, posting and reporting. Go services work best when each does one of those, with a clear contract. That split needs evidence: which fields does each rule read, which records does it write, and which rules always run together. PYD ENTERPRISES' rule clustering gives you that evidence with source links, and architects decide the final boundaries.

PYD ENTERPRISES does not generate Go code. It produces DMN tables, JSON Schema and OpenAPI contracts and JSON fixtures. Many Go teams generate structs from the JSON Schema with their usual tools and write handlers by hand against the decisions.

Why the fixture boundary is 50000.00

The condition is WR-AMOUNT > 50000, strictly greater. A transfer of exactly 50,000.00 is charged 35.00, not 45.00. The fixture generator reads the comparison operator and the field's scale (S9(9)V99, so the next representable value is 50000.01) and emits fixtures on both sides of the boundary. Off-by-one-cent errors at thresholds are among the most common defects after a rewrite, and these fixtures catch them before production does.

FAQ

COBOL to Go microservices questions

Does PYD ENTERPRISES write Go code?

No. It produces decisions, contracts and fixtures. Your engineers write the Go services, often generating structs from the exported JSON Schema with standard tools.

How should we represent COBOL decimals in Go?

Use a decimal library or scaled integers consistently. The spec records each field's digits, scale and rounding, so whichever choice you make can be tested against the fixtures.

Can the fixtures run against the COBOL program too?

Yes. The same fixtures can drive the legacy program through a test harness on the mainframe or a compatible runtime, which confirms the fixtures themselves before the Go service is tested.

Get the spec for your COBOL estate.

Request a codebase scan and review extracted rules, contracts and fixtures for your own COBOL estate, inside your VPC.