The only REQUIRED paragraph in the IDENTIFICATION DIVISION. Every COBOL program must have a PROGRAM-ID. It's the name the operating system uses to call the program.
These are all optional but good practice for documentation:
PITFALL: COBOL columns still matter on mainframes. Columns 1-6 = sequence numbers, col 7 = indicator area (* comment, - continuation, D debug), cols 8-11 = Area A (DIVISION, SECTION, paragraph names), cols 12-72 = Area B (statements). Free format is allowed on most modern compilers.
Documents what computer compiled it and what computer runs it. On modern systems (GnuCOBOL, Micro Focus) these are mostly documentation — but on mainframes, the OBJECT-COMPUTER can affect code generation.
The FILE-CONTROL paragraph maps internal file names (used in the DATA DIVISION) to external filenames or DD names. This is where you define file organisation — crucial for getting I/O right.
| Organization | Use Case |
|---|---|
| LINE SEQUENTIAL | Text files, one record per line |
| SEQUENTIAL | Fixed-length records, no delimiters |
| INDEXED | Keyed access — like a simple database |
| RELATIVE | Record number access — position-based |
Global variables for your program. Defined with level numbers and PICTURE clauses.
The PICTURE (PIC) clause defines the data type and size. Every elementary item needs one.
| Symbol | Meaning | Example |
|---|---|---|
| 9 | Numeric digit (0-9) | PIC 9(4) = 4 digits |
| X | Alphanumeric character | PIC X(30) = 30 chars |
| A | Alphabetic (A-Z, space) | PIC A(20) |
| S | Signed numeric | PIC S9(5) = ±99999 |
| V | Implied decimal point | PIC 9(3)V99 = 999.99 |
| P | Scaling position | PIC 9(4)PP = ×100 |
PITFALL: PIC 9(3)V99 stores 5 bytes (3+2) but the V is implied — there's no actual decimal point in storage. COBOL aligns the decimal in arithmetic. Getting V wrong is how you bill someone £999.99 when you meant £9.99.
Level numbers create the record hierarchy. Group items (01, 05, 10) contain subordinate items.
| Level | Use |
|---|---|
| 01 | Record description — top level |
| 02-49 | Group or elementary items |
| 66 | RENAMES clause |
| 77 | Standalone elementary item (no group) |
| 88 | Condition name — not a variable, a condition |
| 78 | CONSTANT (some compilers) |
PITFALL: Level 88 is NOT a variable — you can't MOVE to it. It's a condition that's TRUE when its parent equals the specified value. Beginners always try to use 88s as variables. Don't be that frog.
REDEFINES lets you view the same bytes with a different structure. Critical for parsing legacy file formats. The redefining item must have the same (or larger) size. Both names share the same storage — changing one changes the other.
Initial values for variables. Without VALUE, numeric fields start as undefined (garbage!).
Common figurative constants: ZEROS, SPACES, HIGH-VALUES, LOW-VALUES, QUOTES, ALL "x"
COBOL's assignment operator. Copies data from source to destination, respecting PICTURE clauses. Automatic type conversion (sort of) — alphanumeric to numeric, truncation or padding as needed.
PITFALL: MOVE SPACES TO a PIC 9 field gives you a runtime error. MOVE "ABC" TO PIC 9 gives you... undefined behaviour. Know your types!
COBOL's arithmetic verbs. More readable than + - * / symbols.
ROUNDED option: ADD 1.005 TO WS-TOTAL ROUNDED — but check the compiler's rounding mode. GnuCOBOL rounds away from zero; some mainframes round to even.
COBOL's control flow — called, executed, and returned from. My dissertation topic! Multiple forms:
PITFALL: In a PERFORM THRU (or THROUGH), don't let execution fall through into the next paragraph. Every paragraph should end with EXIT, EXIT PROGRAM, or GO TO. Falling through is the COBOL equivalent of a missing return statement.
END-IF terminates the IF block. Without it, the IF spills into the next statement! Always pair IF with END-IF. Nest freely but format carefully.
COBOL's version of switch/case — much cleaner than nested IFs.
EVALUATE also supports TRUE/FALSE conditions, ranges, and multiple conditions. Very powerful. My Bog Valley lecturer called it "COBOL's hidden gem."
GO TO branches unconditionally. It works, but structured programming (PERFORM) is almost always better. In the billing system, expect to see GO TO used for error handling and early exits — that's the legacy pattern. Don't write new code with it unless Froggy's system expects it.
PITFALL: GO TO out of a PERFORM paragraph breaks the call stack. ALWAYS use EXIT PARAGRAPH or EXIT SECTION instead of GO TO to return from a performed paragraph.
Level 88 condition names make IF statements read like English.
Calling subprograms. Parameters must match the called program's LINKAGE SECTION. This is how COBOL modules talk to each other — expect to see CALL in the billing system's main dispatch module.
EXIT PROGRAM returns control to the caller. STOP RUN terminates the entire run unit.
PITFALL: READ AT END fires when there are no more records. Always handle this — without it, a READ past EOF causes a runtime error. The FILE STATUS variable helps: 00 = success, 10 = end of file, 30 = permanent error.
FILE STATUS is the most important thing you can check. Without it, a failed READ silently produces garbage data. Froggy once told me: "Every file operation without a FILE STATUS check is a bug waiting to become a support ticket." He was right.
FILE STATUS 92 means the dataset is still allocated from a previous job run that didn't properly CLOSE or release it. This is NOT a program bug — it's an operational issue. The fix: release the dataset (via the operator console or JCL) and resubmit. Froggy told me: "The most common 'program error' I see is actually FILE STATUS 92. The program is fine. The tape wasn't released." Common causes: job ABEND'd without cleanup, operator forgot to dismount overnight tape, or two jobs share the same dataset name.
REEL status is a tape-specific check that verifies the physical tape is mounted before any I/O begins. Unlike a DISK file, a tape drive requires human intervention to mount the reel. A well-designed system does NOT crash if the tape is missing — it waits for a human operator to mount it, then proceeds. The WAIT statement is COBOL's way of saying "pause until a human fixes this." The INIT phase of a billing pipeline always includes: (1) allocate files, (2) check REEL status, (3) validate date parameters. If any of those fail, do not proceed.
STRING concatenates multiple pieces into one field. Each piece is delimited (usually by SPACES or SIZE). ON OVERFLOW fires if the target is too small.
UNSTRING splits a field into multiple targets based on delimiters. The delimiter itself is consumed. Multiple delimiters can be specified with OR. COUNTING and POINTER options give you fine control.
INSPECT is COBOL's text processing swiss army knife. TALLYING counts, REPLACING substitutes. CHARACTERS, ALL, LEADING, FIRST give you precision.
Test what kind of data a field contains — before using it.
IS NUMERIC, IS ALPHABETIC, IS ALPHABETIC-UPPER — vital for validating input from files. IS NUMERIC returns false if the field has SPACES or non-numeric characters.
IS POSITIVE (> 0), IS NEGATIVE (< 0), IS ZERO (= 0). Works with signed numeric fields (PIC S9...).
AND, OR, NOT work as expected. Parentheses for grouping: IF (A AND B) OR (C AND D).
Money in COBOL uses signed numeric fields with an implied decimal point. Get this wrong and you bill someone £999.99 for a £9.99 item — or worse, the reverse.
| Pattern | Size | Max Value | Use Case |
|---|---|---|---|
| PIC S9(5)V99 | 8 digits | £99,999.99 | Line items, discounts |
| PIC S9(7)V99 | 10 digits | £9,999,999.99 | Invoices, customer balances |
| PIC S9(9)V99 | 12 digits | £999,999,999.99 | Batch totals, running sums |
| PIC S9(11)V99 | 14 digits | £99,999,999,999.99 | Ledger-level grand totals |
⚠ THE IMPLIED DECIMAL PITFALL: PIC 9(3)V99 stores 5 bytes but the decimal is implied — there's no dot in storage. If you display a PIC 9(3)V99 with value 12345, you see "12345" not "123.45". COBOL tracks the decimal position in arithmetic, but file output and DISPLAY show the raw digits. In the billing system, look for how they handle decimal display — there's usually a separate display field with an explicit decimal.
Froggy's Rule: "Always check your V positions before and after arithmetic. One off-by-one decimal error and you've billed someone a thousand times what they owe. I've seen it happen. Twice."
COBOL's ROUNDED option controls what happens when the result has more decimal places than the target field.
CRITICAL: Different COBOL compilers round differently! GnuCOBOL rounds away from zero (0.005 → 0.01). Some mainframe compilers round to even (banker's rounding: 0.005 → 0.00, 0.015 → 0.02). In a billing system, this difference can cost (or save) real money over millions of transactions.
Ask Froggy on Monday: "What rounding mode does the billing compiler use? Is there a rounding standard documented somewhere?"
The standard batch billing pattern. Every billing run follows this structure:
Froggy's Rule: "Every billing run needs THREE outputs: the billing file (success), the error report (failures), and a control total (did the numbers balance?). If you only have two, you're missing reconciliation data."
Billing systems MUST handle errors gracefully — one bad record shouldn't kill the whole batch.
Key principles for billing error handling:
Ask Froggy on Monday: "How does the billing system handle partial failures — does it write an audit trail for every skipped record? What's the threshold before it ABENDs the whole batch?"