COBOL 101: Hello World, Taxes, and Why Mainframes Hate Prompts


As I mentioned in my previous post, there is a profound sense of peace in the rigid, unyielding structure of a mainframe. It’s an antidote to the chaos of modern tech stacks (and modern life).

Because a few of you asked how to actually get started with this "ancient" language without paying IBM thousands of dollars for z/OS access, I am starting a short tutorial series. We will write real COBOL, compile it locally, and look at how it translates to the enterprise world.

To follow along, you don't need a mainframe. You just need GnuCOBOL, a free, open-source compiler. If you are on Linux, it's as simple as:

sudo apt install gnucobol

Let’s dive into the absolute basics.

1. The Rules of the Punch Card

Before we write code, you must understand the formatting. COBOL was born in 1959, an era of punch cards. The compiler expects certain things in certain columns:

  • Columns 1-6: Sequence numbers (mostly ignored today, just leave them blank).

  • Column 7: The indicator area. An asterisk * here means the line is a comment.

  • Columns 8-11 (Area A): Used for major structural headers like Divisions, Sections, and Paragraph names.

  • Columns 12-72 (Area B): Used for actual executable statements and logic.

2. Hello, World

Every COBOL program is divided into four strictly ordered Divisions. Let's write our first program. Save this as hello.cbl:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. HELLOW.
       
       ENVIRONMENT DIVISION.
       
       DATA DIVISION.
       
       PROCEDURE DIVISION.
      * Look at the spaces! DISPLAY starts in Area B (Column 12)
           DISPLAY "HELLO, WORLD.".
           STOP RUN.

To compile and run it locally:

cobc -x -o hello hello.cbl
./hello

(-x tells the compiler to build an executable rather than a library).

Congratulations, you are now officially writing code that powers the global financial system.

3. The Interactive Tax Calculator

Let's build something slightly more useful. We want a program that asks the user for a price, calculates a 19% tax, and displays the total.

To do this, we need variables. In COBOL, variables live in the WORKING-STORAGE SECTION of the DATA DIVISION. We define them using PICTURE (or PIC) clauses to tell the compiler exactly how many bytes and what data type to allocate.

Save this as taxcalc.cbl:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. TAXCALC.
       
       DATA DIVISION.
       WORKING-STORAGE SECTION.
      * 9 means numeric. V is the implied decimal point.
       01 WS-PRICE        PIC 9(5)V99.
       01 WS-TAX-RATE     PIC V999    VALUE .190.
       01 WS-TAX-AMOUNT   PIC 9(4)V99.
       01 WS-TOTAL        PIC 9(6)V99.
       
      * Formatted output variables
       01 WS-PRINT-TOTAL  PIC ZZZ,ZZ9.99.

       PROCEDURE DIVISION.
           DISPLAY "ENTER BASE PRICE (e.g. 0150050 for 1500.50): ".
           ACCEPT WS-PRICE.
           
           COMPUTE WS-TAX-AMOUNT = WS-PRICE * WS-TAX-RATE.
           COMPUTE WS-TOTAL = WS-PRICE + WS-TAX-AMOUNT.
           
           MOVE WS-TOTAL TO WS-PRINT-TOTAL.
           
           DISPLAY "------------------------".
           DISPLAY "TAX (19%):  " WS-TAX-AMOUNT.
           DISPLAY "TOTAL DUE:  " WS-PRINT-TOTAL.
           
           STOP RUN.

Compile and run it:

cobc -x -o taxcalc taxcalc.cbl
./taxcalc

It works beautifully on your local Linux or Windows terminal. The ACCEPT statement halts execution, waits for your keyboard input, and then proceeds.

4. The Mainframe Reality Check: Why JCL Hates "ACCEPT"

Here is where the local development experience diverges from enterprise reality.

If you take that exact taxcalc.cbl program, upload it to an IBM Mainframe, and run it via JCL (Job Control Language), it will fail. It will likely freeze, time out, or throw an ABEND (Abnormal End).

Why? Because mainframes are batch processors, not personal computers.

When you submit a job via JCL, it goes into a queue. At 2:00 AM, the system scheduler picks it up, allocates CPU time, and runs it in the background. There is no human sitting at a console waiting for the "ENTER BASE PRICE" prompt. The system hits the ACCEPT statement, sees no standard input attached to a keyboard, and crashes.

How Mainframes Actually Handle Input In the enterprise world, you don't prompt users. You read data from files or data streams. Instead of waiting for a keyboard, a JCL script uses a DD (Data Definition) statement to feed input directly into the program's execution stream.

If we modified our program to read from standard input without prompting, the JCL to feed it data would look like this:

Code snippet

//TAXJOB   JOB (ACCT123),'CALC TAXES',CLASS=A,MSGCLASS=X
//STEP1    EXEC PGM=TAXCALC
//STEPLIB  DD DSN=DOCKERR.LOADLIB,DISP=SHR
//SYSOUT   DD SYSOUT=*
//* Here is the magic: In-stream data replacing human input!
//SYSIN    DD *
0150050
0025000
0999999
/*

The //SYSIN DD * tells the mainframe: "Hey, whenever the program asks for input, don't wait for a human. Just read the next line from this script." This is the core philosophy of enterprise computing. You process millions of records sequentially and silently. No prompts. No waiting. Just pure, unadulterated throughput.

In the next part of this series, we will strip out the ACCEPT statements entirely and look at how COBOL defines, opens, and processes massive sequential files—the true bread and butter of the language.

Stay tuned.


← Back to COBOL & Z Server