Coding Standards and Best Practices
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the section bibliography. |
This page is written against ISO/IEC 1989:2023 (edition 3, January 2023; an upcoming 4th edition is in ballot), in which explicit scope terminators and condition names come from COBOL 85 and the in-line PERFORM and free-form source come from COBOL 2002; against IBM Enterprise COBOL for z/OS 6.5 (PTFs through September 2026) on z/OS 3.2; and against GnuCOBOL 3.2 (28 July 2023, the latest release), with which the program on it was compiled with cobc -x -Wall (and with -std=ibm) and run on 2026-10-20 without warnings. There is no official COBOL style standard: the conventions below are the common practice of shops and courses, restated here, and a team should agree its own list and apply it with a review checklist.
COBOL programs live for decades and are read far more often than written, usually by someone who did not write them. The rules that matter most are therefore the ones that make a program easy to read top to bottom, hard to break by a small edit, and safe against bad input. The page gives those rules, then one complete program that follows them, and a checklist for review. The statements are described on PERFORM and Structured Programming, Conditions: IF and EVALUATE, VALUE, INITIALIZE and Condition Names and Copybooks and REPLACE; testing the result is on Testing COBOL Programs.
Where the book is outdated: Taulli’s Modern Mainframe Development (2022, chapter 4) teaches the language elements in the order of the divisions and shows GO TO and paragraph fall-through among the control-flow forms. They are valid and still compile, but modern practice is the structured style below; old constructs and their replacements are collected, labelled "old code you may meet", on Legacy Features and Migration.
Layout and structure
-
One idea per paragraph. A paragraph or section does one named thing (
2100-READ-LOAN,2200-VALIDATE-LOAN). If its name needs "and", split it. -
One period per paragraph. Write statements without full stops inside a paragraph and put one period at the end. A stray period in the middle of an
IFends it and makes the rest unconditional, which is the oldest bug in COBOL. -
Explicit scope terminators. Close
IFwithEND-IF,READwithEND-READ,EVALUATEwithEND-EVALUATE,PERFORMwithEND-PERFORM,COMPUTEwithEND-COMPUTEand so on, so that nesting is visible and an inserted statement lands where it looks. -
Structured control flow. Use
PERFORMof paragraphs and in-linePERFORM ... END-PERFORM,EVALUATEandIF. Do not useGO TOandALTERfor new code; they are covered only as old code on the legacy page. -
Numbered paragraphs in reading order. A numeric prefix (
1000-,2000-) shows the structure and the order in listings, and a main paragraph at the top reads as the table of contents of the program. -
One entry, one exit. Leave the program in one place (
STOP RUNorGOBACK), after closing files and settingRETURN-CODE; the one exception worth making is a hard stop on an I/O failure, as the example does. -
Keep to the format. In fixed format, code starts in area A for divisions, sections and paragraph names and in area B for statements (column 12), and nothing is written past column 72; see Reference Format: Fixed and Free.
Names
| Convention | Example |
|---|---|
Prefix by storage area |
|
Prefix by kind |
|
Names say what, not how |
|
Condition names read like a question |
|
Copybook names are the record names |
|
One spelling per concept |
|
Names up to 30 characters are allowed in Enterprise COBOL (GnuCOBOL allows longer ones); use them to be clear and abbreviate only by a rule written down. Do not reuse reserved words, and avoid names that differ only in a hyphen.
Data
-
Flags are
PIC Xwith 88-levels, not literals.SET WS-AT-END TO TRUEandIF WS-AT-ENDreplace tests against"Y"scattered through the program, and the values are defined once. -
Record layouts come from copybooks. Every program that reads
LOAN-RECORDcopiesLOANREC, so a change to the file format is one edit and one recompilation of the programs that use it. Share a copybook, not a pasted copy; see the review rules at the end for changing a shared copybook. -
Initialise explicitly. Give
VALUEclauses to counters and switches, and useINITIALIZEfor work areas that are reused;INITCHECKon z/OS and the GnuCOBOL-Wallwarnings find the ones that were missed. -
Size numerics for the largest value including totals, and give money a sign and decimals:
PIC S9(7)V99. A counter that overflows its picture silently wraps. -
Group items for related fields,
77-levels never. A01group with05items documents which fields belong together, andREDEFINESandMOVE CORRESPONDINGcan use the group. -
No magic numbers. Put the fine rate and cap, and table sizes, in named items.
Defensive input and output
-
Always code
FILE STATUSon every file, and test it afterOPEN,READ,WRITEandCLOSE. A program without a status item stops with a run-time error when the file is wrong; a program with one and no test carries on with bad data. The codes are in File Status Codes. -
Treat end of file as normal and everything else as an error. The example below accepts status
00and10and stops on any other value after printing it. -
Set
RETURN-CODEdeliberately (0success,4warnings such as rejected records,8or12failure) and document the meaning in the program header, so the job’sCOND/IFtests are reliable. -
Validate numerics before arithmetic. Test numeric fields from files with the class condition
IF field IS NUMERICbefore computing with them; non-numeric data is the cause ofS0C7abends on z/OS (see Debugging and Troubleshooting). -
Code
ON SIZE ERRORon divisions and on additions that can exceed the receiving field, or the result is silently truncated. -
Use
EVALUATEfor decision tables. A table of conditions and actions (here: type, member, book) is a flatEVALUATE TRUEwithWHENbranches and aWHEN OTHER, not nested `IF`s. -
Reject, do not repair. Write bad records to a rejects file or the report with the reason, and let the nightly run continue; do not guess a corrected value.
A complete example
The program validates the loan transactions: a record is good when the type is O (checkout) or R (return) and both identifiers are numeric. It follows the rules above: numbered single-purpose paragraphs, a priming read, 88-level flags, a copybook for the layout, a status test after every file operation, an EVALUATE for the rules, and a return code of 4 when something was rejected.
* VALLOAN - validates the daily loan transactions.
IDENTIFICATION DIVISION.
PROGRAM-ID. VALLOAN.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT LOAN-FILE ASSIGN TO "LOANTRAN"
ORGANIZATION IS LINE SEQUENTIAL
FILE STATUS IS WS-LOAN-STATUS.
DATA DIVISION.
FILE SECTION.
FD LOAN-FILE.
COPY LOANREC.
WORKING-STORAGE SECTION.
01 WS-LOAN-STATUS PIC XX.
88 WS-LOAN-OK VALUE "00".
88 WS-LOAN-EOF VALUE "10".
01 WS-SWITCHES.
05 WS-EOF-FLAG PIC X VALUE "N".
88 WS-AT-END VALUE "Y".
88 WS-NOT-AT-END VALUE "N".
01 WS-COUNTERS.
05 WS-READ-COUNT PIC 9(5) VALUE 0.
05 WS-GOOD-COUNT PIC 9(5) VALUE 0.
05 WS-REJECT-COUNT PIC 9(5) VALUE 0.
PROCEDURE DIVISION.
0000-MAIN.
PERFORM 1000-OPEN-FILE
PERFORM 2000-PROCESS-LOANS UNTIL WS-AT-END
PERFORM 3000-CLOSE-AND-REPORT
IF WS-REJECT-COUNT > 0
MOVE 4 TO RETURN-CODE
END-IF
STOP RUN.
1000-OPEN-FILE.
OPEN INPUT LOAN-FILE
IF NOT WS-LOAN-OK
DISPLAY "OPEN FAILED, STATUS " WS-LOAN-STATUS
MOVE 12 TO RETURN-CODE
STOP RUN
END-IF
PERFORM 2100-READ-LOAN.
2000-PROCESS-LOANS.
PERFORM 2200-VALIDATE-LOAN
PERFORM 2100-READ-LOAN.
2100-READ-LOAN.
READ LOAN-FILE
AT END SET WS-AT-END TO TRUE
NOT AT END ADD 1 TO WS-READ-COUNT
END-READ
IF NOT WS-LOAN-OK AND NOT WS-LOAN-EOF
DISPLAY "READ FAILED, STATUS " WS-LOAN-STATUS
MOVE 12 TO RETURN-CODE
STOP RUN
END-IF.
2200-VALIDATE-LOAN.
EVALUATE TRUE
WHEN NOT LOAN-CHECKOUT AND NOT LOAN-RETURN
PERFORM 2900-REJECT
WHEN LOAN-MEMBER-ID IS NOT NUMERIC
PERFORM 2900-REJECT
WHEN LOAN-BOOK-ID IS NOT NUMERIC
PERFORM 2900-REJECT
WHEN OTHER
ADD 1 TO WS-GOOD-COUNT
END-EVALUATE.
2900-REJECT.
ADD 1 TO WS-REJECT-COUNT
DISPLAY "REJECT " LOAN-TYPE " " LOAN-MEMBER-ID
" " LOAN-BOOK-ID.
3000-CLOSE-AND-REPORT.
CLOSE LOAN-FILE
DISPLAY "READ " WS-READ-COUNT " GOOD " WS-GOOD-COUNT
" REJECTED " WS-REJECT-COUNT.
The input is the four-line LOANTRAN of the Circulation scenario plus a fifth line whose type is X. Compiled with cobc -x -Wall -I. VALLOAN.cob (and with -std=ibm), no warnings, it prints the following and ends with exit status 4:
REJECT X 000103 000002
READ 00005 GOOD 00004 REJECTED 00001
Run in a directory without LOANTRAN the program prints OPEN FAILED, STATUS 35 and ends with status 12. The structure is the point: 0000-MAIN shows the whole flow in five lines, and a change to one rule is a change to one WHEN.
Comments and documentation
-
A header comment per program: purpose, inputs and outputs (data sets or files and their copybooks), return codes, the author or team and the change history with the ticket reference. The version-control history is the authority for who changed what; the header says why the program exists.
-
Comment the why, not the what.
ADD 1 TO WS-GOOD-COUNTneeds no comment; "loyalty members joined before 2021 get the discount" does. -
Section comments for the divisions are noise; put the effort in the paragraph names.
-
Keep comments and code together. An out-of-date comment is worse than none. In fixed format a comment has
*in column 7; in free format*>anywhere. -
Do not leave dead code in comments; version control remembers it.
Review checklist
-
Does every file have
FILE STATUS, and is it tested after each operation? -
Does each
IF,READ,EVALUATE,PERFORMand arithmetic statement have its scope terminator, and is there exactly one period per paragraph? -
Are numeric fields from outside tested before use, and does arithmetic that can overflow have
ON SIZE ERROR? -
Are layouts from copybooks, and do the edits to a shared copybook list every program that uses it so that all of them are recompiled and tested? A copybook used by many programs should be changed only with a review of its users.
-
Are flags 88-levels and are literal codes ("O", "R") only in the copybook?
-
Does the program set a documented
RETURN-CODE? -
Does it compile with
-Wall(GnuCOBOL) and with the project’s standard Enterprise COBOL options without new warnings? -
Is there a test for the new rule (Testing COBOL Programs)?
Common mistakes
-
Nesting
IF`s without `END-IFand a stray period. The statements after the period are outside everyIF. -
Editing a paragraph in the middle of a
PERFORM THRUrange. A new paragraph inserted between the named ends becomes part of the range; usePERFORMof one paragraph. -
Reusing a work field for two meanings. A field that is a count in one paragraph and a date in another hides defects; define one field per meaning.
-
Testing a flag with a literal in one place and an 88-level in another.
-
Style fixes mixed with logic changes in one commit. Reviewers cannot see the logic change; make the formatting change on its own.