Skip to content
PYD ENTERPRISES
Engineering8 min read

Synthesizing OpenAPI 3.1 from CICS COMMAREAs

Thousands of CICS programs already behave like services: a caller passes a block of bytes, the program fills in part of it and returns. The contract exists. It was just never written down.

Pranav KulkarniHead of Language Engineering, PYD ENTERPRISES
On this page · 6 sections
  1. 01The COMMAREA is an interface
  2. 02Dataflow decides what is input and what is output
  3. 03PICTURE to JSON Schema
  4. 04The generated operation
  5. 05Pseudo-conversational programs are a different case
  6. 06Channels, containers and the 32 KB limit

The COMMAREA is an interface

A CICS program invoked with EXEC CICS LINK receives its input in DFHCOMMAREA, declared in the LINKAGE SECTION, and returns by writing into the same storage before EXEC CICS RETURN. The caller sees the updated area when control comes back. That is a request and a response sharing one buffer, with the layout defined by a copybook both sides include.

This shape is why CICS inquiry and update programs are good candidates for API exposure. It is also why naive conversion produces bad specs. Converting the copybook straight into a request schema and a response schema gives you two identical objects with every field optional, which tells a consumer nothing.

LNINQ01.cbl (linkage and entry)COBOL
1 LINKAGE SECTION.
2 01 DFHCOMMAREA.
3 05 CA-REQUEST.
4 10 CA-LOAN-ID PIC X(12).
5 10 CA-AS-OF-DATE PIC 9(08).
6 05 CA-RESPONSE.
7 10 CA-RETURN-CD PIC 9(02).
8 88 CA-OK VALUE 00.
9 88 CA-NOT-FOUND VALUE 04.
10 88 CA-CLOSED VALUE 08.
11 10 CA-PRIN-BAL PIC S9(11)V99 COMP-3.
12 10 CA-ACCRUED-INT PIC S9(9)V99 COMP-3.
13 10 CA-NEXT-DUE-DATE PIC 9(08).
14
15 PROCEDURE DIVISION.
16 IF EIBCALEN < LENGTH OF DFHCOMMAREA
17 EXEC CICS ABEND ABCODE('LNQ1') END-EXEC
18 END-IF.

Dataflow decides what is input and what is output

The names CA-REQUEST and CA-RESPONSE are a courtesy. Plenty of programs interleave input and output fields, reuse an input field as a work area, or echo part of the request. The synthesizer does not trust names. It classifies each COMMAREA field by dataflow over every path from program entry to every RETURN.

  • A field read before any write on some path is an input.
  • A field written on some path before return is an output.
  • A field written on every path is a required output; written on some paths only, an optional output with the conditions recorded.
  • A field both read first and later written is in/out and appears in both schemas.

For LNINQ01, CA-LOAN-ID and CA-AS-OF-DATE are inputs. CA-RETURN-CD is written on every path. The balance fields are written only when CA-OK is set, which the synthesizer turns into a response variant rather than a set of optional properties.

PICTURE to JSON Schema

OpenAPI 3.1 aligns fully with JSON Schema 2020-12, which removes the old nullable keyword and lets us use standard constructs such as const, oneOf and type arrays. The mapping rules are deliberately conservative.

  • PIC X(n): type: string, maxLength: n. Trailing spaces are trimmed on output and padded on input by the adapter, and the schema says so in a description.
  • PIC 9(n) with no decimal and no arithmetic use: string with a digit pattern, because leading zeros are often significant in identifiers.
  • Signed or scaled numerics, including COMP-3: string with a decimal pattern whose integer and fraction lengths come from the PICTURE. Amounts never become JSON numbers.
  • 88-level values on a return code: an enum of the code values, plus an x-pyd-conditions extension mapping each value to its condition name.
  • Eight-digit date fields used as CCYYMMDD in procedure code: format: date.

The generated operation

Inquiry programs that write nothing durable become GET operations; programs whose slice reaches a REWRITE, WRITE or SQL UPDATE become POST or PUT, and the decision is shown for review. Return codes map to HTTP status codes only where the mapping is unambiguous. CA-NOT-FOUND becomes 404. CA-CLOSED is a business outcome, not an error, so it stays a 200 response with a status discriminator unless the reviewer decides otherwise.

loan-inquiry.openapi.yaml (excerpt)YAML
1openapi: 3.1.0
2info: { title: Loan inquiry, version: 1.0.0 }
3paths:
4 /loans/{loanId}/position:
5 get:
6 operationId: getLoanPosition
7 x-pyd-source: { program: LNINQ01, file: LNINQ01.cbl, lines: [41, 118] }
8 parameters:
9 - { name: loanId, in: path, required: true, schema: { type: string, maxLength: 12 } }
10 - { name: asOfDate, in: query, required: true, schema: { type: string, format: date } }
11 responses:
12 "200":
13 content:
14 application/json:
15 schema:
16 oneOf:
17 - $ref: "#/components/schemas/LoanPositionOpen"
18 - $ref: "#/components/schemas/LoanPositionClosed"
19 "404": { description: CA-NOT-FOUND (return code 04) }
20components:
21 schemas:
22 LoanPositionOpen:
23 type: object
24 required: [status, principalBalance, accruedInterest, nextDueDate]
25 properties:
26 status: { const: OK }
27 principalBalance: { type: string, pattern: "^-?[0-9]{1,11}\\.[0-9]{2}$" }
28 accruedInterest: { type: string, pattern: "^-?[0-9]{1,9}\\.[0-9]{2}$" }
29 nextDueDate: { type: string, format: date }
30 LoanPositionClosed:
31 type: object
32 required: [status]
33 properties:
34 status: { const: CLOSED }

Pseudo-conversational programs are a different case

Not every COMMAREA is an API. Programs behind a BMS screen usually run pseudo-conversationally: they send a map, issue EXEC CICS RETURN TRANSID(...) COMMAREA(...), and pick up their state from the COMMAREA when the user presses Enter. That COMMAREA is session state, not a contract. The synthesizer detects the pattern from the RETURN TRANSID and the EIBCALEN = 0 first-time branch, and models the program as a state machine whose transitions are screen interactions. The business rules inside each transition are extracted normally; the state itself is documented for the team designing the new user journey rather than exposed as an endpoint.

Channels, containers and the 32 KB limit

Newer CICS applications pass data in channels and containers to get past the COMMAREA size limit of roughly 32 KB. The same dataflow classification applies per container: GET CONTAINER before any PUT CONTAINER marks an input, and each container gets its own schema component. Programs that pass a pointer in the COMMAREA to storage obtained with GETMAIN are flagged for review, because the layout of the pointed-to storage cannot always be resolved statically.

The result is a spec a gateway team can implement against, with every property traceable to a copybook line. The CICS to REST migration guide and the OpenAPI synthesizer page cover the rest of the pipeline.

Keep reading

All posts

Legacy anti-patterns catalog

25 legacy anti-patterns, mapped to modern designs.

From COBOL ALTER and GO TO DEPENDING ON to EJB entity beans and PL/SQL cursors: why each is dangerous during migration, how it is detected, and the before and after code.

Explore the catalog

See your own code as a rule inventory.

Run PYD ENTERPRISES on up to 100,000 lines of COBOL, Java or .NET in your private VPC and review the extracted rules beside their source lines.