The Predictable Beauty of COBOL and JCL


Lately, the real world has proven to be an exhausting, unpredictable mess. Between the grief, the Maltese grifters, and the corporate vultures in Germany gambling with death benefits, human systems feel fundamentally broken.

When things get too chaotic, I retreat into my hobbies. For me, that’s C++ and COBOL.

I am IBM Certified in COBOL, and I genuinely love working with it. There is something profoundly comforting about a language that hasn't fundamentally changed its core philosophy since the 1960s. It doesn't care about the latest JavaScript framework, and it doesn't try to be clever. It just processes billions of transactions a day, flawlessly, without complaining.

If you've never touched a mainframe, here is a look at how we write code, do math, and execute jobs in the world of z/OS.

  1. The Mandatory "Hello, World"

COBOL (Common Business-Oriented Language) was designed to be readable by management (a noble but doomed goal from 1959). It is strictly structured into "Divisions." Furthermore, because it originates from the era of punch cards, formatting matters. Code generally starts in Area A (column 8) or Area B (column 12).

Here is the absolute minimum required to print a string:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. HELLOW.
       
       ENVIRONMENT DIVISION.
       
       DATA DIVISION.
       
       PROCEDURE DIVISION.
      * This is a comment. Notice the asterisk in column 7.
           DISPLAY 'HELLO, WORLD'.
           STOP RUN.

No #include, no int main(), no messing around with namespaces. You identify the program, state your procedures, and stop the run.

  1. Why COBOL Rules the Financial World: Arithmetic

If you ever wonder why banks and insurance companies still run COBOL, the answer is simple: Fixed-point decimal arithmetic.

In C++ or Python, if you add 0.1 + 0.2 using standard floating-point numbers, you get 0.30000000000000004. That rounding error is unacceptable when you are calculating interest for millions of bank accounts. COBOL uses PICTURE (or PIC) clauses to define exactly how many digits a number has, and it does the math accurately at the hardware level (using packed decimals).

Let's do some math:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. MATHDEMO.
       
       DATA DIVISION.
       WORKING-STORAGE SECTION.
      * 9 means numeric. V is the implied decimal point.
       01 WS-PRICE        PIC 9(4)V99 VALUE 1500.50.
       01 WS-TAX-RATE     PIC V999    VALUE .075.
       01 WS-TAX-AMOUNT   PIC 9(3)V99.
       01 WS-TOTAL        PIC 9(5)V99.
       
      * For printing to the screen, we format it with actual dots and commas
       01 WS-PRINT-TOTAL  PIC $$,$$9.99.

       PROCEDURE DIVISION.
           COMPUTE WS-TAX-AMOUNT = WS-PRICE * WS-TAX-RATE.
           COMPUTE WS-TOTAL = WS-PRICE + WS-TAX-AMOUNT.
           
      * We can also use verbose verbs instead of COMPUTE:
      * MULTIPLY WS-PRICE BY WS-TAX-RATE GIVING WS-TAX-AMOUNT.
      * ADD WS-PRICE TO WS-TAX-AMOUNT GIVING WS-TOTAL.
           
           MOVE WS-TOTAL TO WS-PRINT-TOTAL.
           
           DISPLAY "BASE PRICE: " WS-PRICE.
           DISPLAY "TAX:        " WS-TAX-AMOUNT.
           DISPLAY "TOTAL DUE:  " WS-PRINT-TOTAL.
           
           STOP RUN.

If you run this, you won't get any floating-point garbage. You get exactly what you expect. Every single penny is accounted for.

  1. Job Control Language (JCL): Waking the Beast Here is the catch: you don't just open a terminal on an IBM Mainframe, type ./MATHDEMO, and hit enter. The mainframe is a massive time-sharing beast that schedules workloads. To tell the mainframe to compile and run your code, you use JCL (Job Control Language).
  • JCL is notorious for being cryptic, but it fundamentally consists of three statements:

  • JOB: Who are you and what account is paying for this CPU time?

  • EXEC: What program or procedure do you want to execute?

  • DD (Data Definition): Where are the input files, and where should the output go?

Here is a JCL script to compile and execute our COBOL program:

//DOCKERRJ JOB (ACCT123),'COMPILE AND RUN',CLASS=A,MSGCLASS=X,NOTIFY=&SYSUID
//* ---------------------------------------------------------
//* STEP 1: COMPILE THE COBOL SOURCE CODE
//* ---------------------------------------------------------
//COMPILE  EXEC IGYWCL
//COBOL.SYSIN DD DSN=DOCKERR.SOURCE.COBOL(MATHDEMO),DISP=SHR
//* ---------------------------------------------------------
//* STEP 2: RUN THE COMPILED PROGRAM
//* ---------------------------------------------------------
//RUNIT    EXEC PGM=MATHDEMO
//STEPLIB  DD DSN=DOCKERR.LOADLIB,DISP=SHR
//SYSOUT   DD SYSOUT=*
//SYSUDUMP DD SYSOUT=*

Breaking it down: NOTIFY=&SYSUID tells the system to send me a message when the job finishes.

IGYWCL is an IBM-supplied procedure that invokes the COBOL compiler and the linker.

DSN (Data Set Name) is the mainframe equivalent of a file path. DISP=SHR means we are sharing read access to the source code.

SYSOUT=* routes the DISPLAY output from our program directly to the system's output spool (so we can read it on our screen).

The Appeal of the Machine

There is no ambiguity here. If a JCL script fails, it fails with a specific return code. If a COBOL program abends (abnormal end), it gives you a memory dump. Pure dumps without any helper - just you and the bare metal. It just executes the logic you gave it, exactly as you wrote it. In times like these, I find that incredibly refreshing.


← Back to COBOL & Z Server