Robust programming in COBOL means writing code that behaves correctly even when data is bad, resources are missing, or operations fail. It relies on defensive coding: validating input, checking every file and command response, validating subscripts and indexes, and handling errors explicitly. This page covers how to make your COBOL programs more reliable through validation, error handling, and defensive traps.
Robust programming is like checking that the door is locked and the lights work before you leave the house. In a program, it means: "Did the read work?" "Is this number in the right range?" "Is the subscript valid?" Instead of assuming everything is fine, you check. When something is wrong, you handle it (show a message, skip the record, or stop safely) instead of letting the program crash or produce wrong answers.
Defensive coding is the habit of checking conditions that could be wrong before you use them. A defensive trap is a specific check you add to catch invalid or unexpected situations. For example: check file status after every READ; validate that a subscript is between 1 and the table size before using it; check that a divisor is not zero before DIVIDE. Traps are often added around risky areas: file I/O, CALL and CICS commands, STRING/UNSTRING, arithmetic, EVALUATE and IF, and SEARCH. The goal is to fail in a controlled way (with a clear error or message) instead of abending or corrupting data.
| Area | What to check | Purpose |
|---|---|---|
| File I/O | File status after OPEN, READ, WRITE, REWRITE, CLOSE | Handle EOF, errors, invalid key |
| Subscripts and indexes | Bounds before using (e.g. 1 to N) | Avoid subscript out of range |
| CALL / CICS | Return code or EIBRESP | Handle missing program or command failure |
| Arithmetic | Divide by zero, overflow, valid operands | Avoid abends and wrong results |
| Input data | Length, type (NUMERIC), range, allowed values | Reject bad data before use |
Validate all data that comes from outside your program: user input, file records, parameters from CALL, and CICS commareas. Validate as early as possible—ideally at the point where the data enters your program or at the start of the procedure that uses it. Use multiple layers: field-level (type, length, range), record-level (all required fields present and consistent), and cross-record or cross-field where needed (e.g. start date before end date).
Use COBOL class conditions: NUMERIC to ensure a field contains only digits (and sign if applicable), ALPHABETIC for letters and spaces, and ALPHANUMERIC if you allow both. Use relation conditions for ranges: IF WS-AMOUNT >= 0 AND WS-AMOUNT <= 999999.99. Use 88-level condition names for allowed values: define 88 VALID-STATUS VALUES 'A' 'B' 'C' and then IF VALID-STATUS. That makes the allowed set explicit and easy to change.
1234567891011121314151617181901 WS-INPUT. 05 WS-ID PIC 9(6). 05 WS-STATUS PIC X(1). 88 VALID-STATUS VALUES 'A' 'B' 'C'. 05 WS-AMT PIC S9(7)V99. *> Field validation IF WS-ID IS NOT NUMERIC MOVE 'Invalid ID' TO WS-MESSAGE PERFORM ERROR-OUT END-IF IF NOT VALID-STATUS MOVE 'Invalid status' TO WS-MESSAGE PERFORM ERROR-OUT END-IF IF WS-AMT < 0 OR WS-AMT > 999999.99 MOVE 'Amount out of range' TO WS-MESSAGE PERFORM ERROR-OUT END-IF
After every file operation (OPEN, READ, WRITE, REWRITE, DELETE, CLOSE), check the file status. If it is not 00 (and not 10 for end-of-file when that is expected), do not assume the operation succeeded. Branch to error handling: log the status, set a message, and either retry, skip the record, or terminate cleanly. Similarly, in CICS programs check EIBRESP (or RESP/RESP2) after every EXEC CICS command that can fail. Use HANDLE CONDITION or NOHANDLE with explicit checks so that unexpected responses do not cause an abend. Robust programs never assume success; they always branch on the actual response.
12345678910111213READ CUST-FILE AT END SET WS-EOF TO TRUE END-READ IF WS-FILE-STATUS = '00' PERFORM PROCESS-RECORD ELSE IF WS-FILE-STATUS = '10' PERFORM END-OF-FILE ELSE DISPLAY 'READ error ' WS-FILE-STATUS PERFORM ABEND-RECOVERY END-IF END-IF
Before using a subscript or index to access a table (OCCURS), ensure it is within bounds. If the table has OCCURS 12 TIMES, the subscript must be between 1 and 12 (inclusive) for standard 1-based subscripting. If you use a variable that could be wrong (e.g. from input or from a computation), test it: IF WS-SUB >= 1 AND WS-SUB <= 12 before using WS-SUB as a subscript. Invalid subscripts can overwrite storage or cause abends. The same applies to indexes: set and validate them before use in SEARCH or in reference modification.
Before DIVIDE or any operation that could divide by zero, check the divisor. If the divisor is zero, skip the operation or use a default and set an error flag. On some platforms, division by zero causes an abend; on others you may get an overflow or undefined result. Also consider overflow: very large results may not fit in the receiving field. Use ON SIZE ERROR (or equivalent) where available and handle the error path. Validate numeric operands (e.g. NUMERIC) when they come from input so you do not perform arithmetic on non-numeric data.
When you detect an error, decide what the program should do: display or log the error, set a return code, skip the record, retry, or terminate. Use a consistent approach: e.g. a single ERROR-PARAGRAPH that logs the condition and either continues or exits. For batch, you might write bad records to an error file and continue; for CICS, you might send a message to the user and RETURN. Document the severity (e.g. warning vs critical) so operations know whether to investigate. Avoid silently ignoring errors; at least log them.
1. Defensive coding in COBOL emphasizes:
2. To ensure a field contains only digits you can use:
3. After every READ you should: