Program Structure and Divisions

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; a 4th edition is upcoming), GnuCOBOL 3.2 and IBM Enterprise COBOL for z/OS 6.5, checked on 2026-10-20. Every program below was compiled with cobc -x -Wall (GnuCOBOL 3.2.0) and run. The ones presented as working compiled without warnings (also under -std=ibm), and the deliberately wrong ones show the real diagnostics. The examples use the Circulation library scenario of this section (BOOKMAST, copybook BOOKREC).

The page covers the skeleton that every COBOL source file shares: which pieces a program is made of, in which order they appear, and how a program ends. The data descriptions are on Data Division and Level Numbers, the statements start with MOVE and Data Movement, and the column rules that make this text legal are on Reference Format: Fixed and Free. If you have not compiled a COBOL program yet, start with Getting Started.

The hierarchy: divisions down to statements

A COBOL source text is a strict hierarchy. A program has up to four divisions, in a fixed order. Divisions contain sections, sections contain paragraphs, paragraphs contain sentences, and sentences contain statements.

flowchart TD P["Program (ends with END PROGRAM)"] P --> ID["IDENTIFICATION DIVISION"] P --> EN["ENVIRONMENT DIVISION (optional)"] P --> DA["DATA DIVISION (optional)"] P --> PR["PROCEDURE DIVISION (optional)"] ID --> IDP["PROGRAM-ID paragraph"] EN --> CS["CONFIGURATION SECTION"] EN --> IO["INPUT-OUTPUT SECTION"] CS --> CSP["SPECIAL-NAMES and REPOSITORY paragraphs"] IO --> IOP["FILE-CONTROL paragraph"] DA --> DS["FILE, WORKING-STORAGE, LOCAL-STORAGE, LINKAGE sections"] PR --> SEC["Section (optional)"] SEC --> PAR["Paragraph"] PAR --> SEN["Sentence (ends with a period)"] SEN --> STM["Statement (verb and operands)"]

The figure above is the containment tree; the table below says what each level is and how it is written.

Level What it is How it is written

Division

One of four top-level parts. They must appear in the order identification, environment, data, procedure.

A header such as PROCEDURE DIVISION. starting in Area A

Section

A named group of paragraphs (procedure division) or of related entries (the other divisions).

name SECTION. in the procedure division; fixed names such as WORKING-STORAGE SECTION. elsewhere

Paragraph

A named group of sentences. In the procedure division the name is yours and is the target of PERFORM. In the identification and environment divisions the names are fixed, such as PROGRAM-ID.

name. starting in Area A

Sentence

One or more statements ended by a period. A period also ends the scope of every open IF, PERFORM or other conditional statement in it.

Statements followed by .

Statement

A verb with its operands, such as MOVE, ADD or PERFORM. Statements inside a sentence are separated by spaces or line breaks only.

MOVE BOOK-PRICE TO WS-PRICE-OUT

Which parts are required differs by standard and compiler. In the ISO standard only PROGRAM-ID is mandatory in the identification division, and the other three divisions can be left out. In practice a main program always has a procedure division, and a program that handles files or data has the other two. Section and paragraph names are the targets that PERFORM can name, and they start in Area A like the division headers; the area rules are on the reference-format page linked above. Procedure division sections are optional: a program can be made of paragraphs only, or of sections that hold paragraphs. Do not mix the two styles in one division, because paragraphs placed before the first section header are not allowed once sections are used.

The IDENTIFICATION DIVISION

The identification division names the program. It starts with IDENTIFICATION DIVISION. (in a free-format source, the abbreviation ID DIVISION. is accepted by Enterprise COBOL and GnuCOBOL) and holds paragraphs. PROGRAM-ID is the only paragraph the current standard and the compilers require.

PROGRAM-ID

The PROGRAM-ID paragraph gives the program-name and optional attributes. Written readably, the forms used in practice are:

Form Meaning

PROGRAM-ID. BOOKLIST.

Plain name. This is the name a CALL "BOOKLIST" statement resolves.

PROGRAM-ID. BOOKLIST IS INITIAL.

Every call starts the program with its data in the state the data division describes (the VALUE clauses are applied again) and with its files closed. It behaves as if the program were cancelled after each call.

PROGRAM-ID. WALK-TREE IS RECURSIVE.

The program may call itself directly or indirectly. Each activation gets its own LOCAL-STORAGE. In Enterprise COBOL, RECURSIVE may be coded only on the outermost program of a compilation unit, not on a nested program or on a program that contains nested programs.

PROGRAM-ID. SHOWER IS COMMON.

Only for a nested program: it may be called by every program inside the same containing program, not only by the one that directly contains it. See the nested programs section below.

PROGRAM-ID. BOOKLIST AS "bk-list".

COBOL 2002 form that gives the external name as a literal, so the name used on a CALL can contain characters (such as lower case letters) that a program-name cannot. It can be followed by an attribute (AS "bk-list" IS INITIAL). GnuCOBOL 3.2 compiles this form, with and without -std=ibm; check the Enterprise COBOL documentation before relying on it on z/OS.

A program-name is a user-defined word, with length limits that each compiler documents. The attributes COMMON and INITIAL can appear together on a nested program, and COMMON and RECURSIVE do not go together because RECURSIVE is for an outermost program.

Obsolete paragraphs

Five old paragraphs exist only to carry text for the human reader: AUTHOR, INSTALLATION, DATE-WRITTEN, DATE-COMPILED and SECURITY. They are flagged obsolete since COBOL 85, are not read by the compiler (the text is a comment-entry), and are still accepted so that old programs keep compiling. Use *> or a column-7 * comment and source control instead. GnuCOBOL says so with the default dialect:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. OLDSTYLE.
       AUTHOR. LIBRARY IT.
       DATE-WRITTEN. 1989-03-01.
       PROCEDURE DIVISION.
           STOP RUN.
author.cob:3: warning: AUTHOR is obsolete in GnuCOBOL [-Wobsolete]
author.cob:4: warning: DATE-WRITTEN is obsolete in GnuCOBOL [-Wobsolete]

This is old code you may meet; the same program compiled with -std=ibm is accepted without a message, because the IBM dialect still supports the paragraphs. Do not write new programs this way.

Source: GnuCOBOL Manual

The ENVIRONMENT DIVISION

The environment division describes what the program depends on outside itself. It has two sections, both optional:

  • The CONFIGURATION SECTION holds the paragraphs SOURCE-COMPUTER, OBJECT-COMPUTER, SPECIAL-NAMES and REPOSITORY.

  • The INPUT-OUTPUT SECTION holds FILE-CONTROL (one SELECT per file) and I-O-CONTROL.

SOURCE-COMPUTER and OBJECT-COMPUTER

These two paragraphs name the machine that compiles and runs the program. They are documentation in nearly every modern compiler. OBJECT-COMPUTER is where the program collating sequence is selected with PROGRAM COLLATING SEQUENCE IS alphabet-name (the alphabet is defined in SPECIAL-NAMES). Enterprise COBOL also accepts the WITH DEBUGGING MODE clause on SOURCE-COMPUTER for the old debugging lines.

SPECIAL-NAMES

SPECIAL-NAMES relates implementor names to your names and sets program-wide conventions. The entries you will meet most are:

Entry Purpose

DECIMAL-POINT IS COMMA

Swaps the roles of the period and the comma in PICTURE strings and numeric literals, as European text conventions need. Only the compile-time meaning changes: the data is still stored as digits.

CURRENCY SIGN IS literal

Chooses the character that the PICTURE currency symbol stands for, instead of the default $. A second form, CURRENCY SIGN IS literal-1 WITH PICTURE SYMBOL literal-2, separates the string shown from the symbol written in the PICTURE (COBOL 2002, Enterprise COBOL). GnuCOBOL 3.2 does not implement that form completely.

CLASS name IS literal-or-range ...

Defines a user class of characters, usable in a class condition such as IF field IS name. Ranges use THRU.

SYMBOLIC CHARACTERS name ARE ordinal ...

Gives a name to a character by its ordinal position in the collating sequence, which starts at 1. So the byte value is the ordinal minus one.

ALPHABET name IS ...

Defines a character set or collating sequence (NATIVE, STANDARD-1, EBCDIC, or a literal list).

mnemonic names, for example C01 IS TOP-OF-PAGE

Relates an implementor-defined device or feature name (printer channel, console, UPSI switch, SYSIN, SYSOUT) to a name used in WRITE ... ADVANCING, DISPLAY ... UPON and ACCEPT ... FROM. The set of valid implementor names is compiler specific.

CRT STATUS IS field, CURSOR IS field

Screen handling entries; see the console input and output page.

Here is a complete program that uses three of the entries, with the other divisions kept minimal. It lists the book master using CLASS for the ISBN check, CURRENCY SIGN for the price and SYMBOLIC CHARACTERS for a separator:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. BOOKLIST.
      * List the Circulation book master with a symbolic currency.
       ENVIRONMENT DIVISION.
       CONFIGURATION SECTION.
       SPECIAL-NAMES.
           CURRENCY SIGN IS "#"
           CLASS ISBN-CHAR IS "0" THRU "9" "X"
           SYMBOLIC CHARACTERS BAR-CHAR ARE 125.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT BOOK-FILE ASSIGN TO "BOOKMAST"
               ORGANIZATION IS LINE SEQUENTIAL
               FILE STATUS IS WS-BOOK-STATUS.
       DATA DIVISION.
       FILE SECTION.
       FD  BOOK-FILE.
       COPY BOOKREC.
       WORKING-STORAGE SECTION.
       01  WS-BOOK-STATUS      PIC XX.
       01  WS-EOF              PIC X VALUE "N".
           88  END-OF-BOOKS    VALUE "Y".
       01  WS-PRICE-OUT        PIC ###,##9.99.
       PROCEDURE DIVISION.
       MAIN-LINE SECTION.
       MAIN-PARA.
           OPEN INPUT BOOK-FILE
           PERFORM UNTIL END-OF-BOOKS
               READ BOOK-FILE
                   AT END SET END-OF-BOOKS TO TRUE
                   NOT AT END PERFORM SHOW-BOOK
               END-READ
           END-PERFORM
           CLOSE BOOK-FILE
           STOP RUN.
       SHOW-BOOK.
           MOVE BOOK-PRICE TO WS-PRICE-OUT
           IF BOOK-ISBN IS ISBN-CHAR
               DISPLAY BOOK-ID BAR-CHAR BOOK-TITLE BAR-CHAR WS-PRICE-OUT
           ELSE
               DISPLAY BOOK-ID " bad ISBN"
           END-IF.
       END PROGRAM BOOKLIST.

It compiled without warnings and printed the real data of the scenario (character 124 is the vertical bar, ordinal 125):

000001|DATA STRUCTURES               |    #25.00
000002|COBOL BASICS                  |    #19.99
000003|BATCH PROCESSING              |    #30.50
000004|OLD LEDGERS                   |    #12.00

Three details in that program are common stumbling blocks.

  • PICTURE characters such as E, P, S, V, X and Z, and the digits, keep their picture meaning, and the compiler rejects them as a currency sign. The first attempt used "E"; GnuCOBOL answers with real diagnostics:

    bad-currency.cob:7: warning: separate currency symbol and currency string is not implemented [-Wpending]
    bad-currency.cob:7: error: invalid character 'E' in currency symbol
    bad-currency.cob:23: error: invalid PICTURE character 'E'
    bad-currency.cob:23: error: a Z or * which is before the decimal point cannot follow E

    That version was deliberately wrong: it used the WITH PICTURE SYMBOL "E" form and E in the picture. The symbol must be a character that is not used in PICTURE strings, and it is written repeated (###,##9.99) in the edited item.

  • Ordinal positions start at 1, so SYMBOLIC CHARACTERS TAB-CHAR ARE 9 is the backspace (byte 8), not the tab. The tab is ordinal 10 (byte 9).

  • The CLASS condition IF BOOK-ISBN IS ISBN-CHAR is true only when every character of the field belongs to the class. A field padded with spaces fails it, so test the exact size or a reference-modified part.

Using DECIMAL-POINT IS COMMA shows how much one entry changes. The numeric literal 12,50 and the edited picture ZZ.ZZ9,99 follow the comma convention, and the program is otherwise unchanged:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. FINEFMT.
       ENVIRONMENT DIVISION.
       CONFIGURATION SECTION.
       SPECIAL-NAMES.
           DECIMAL-POINT IS COMMA.
       REPOSITORY.
           FUNCTION UPPER-CASE TRIM INTRINSIC.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-NAME             PIC X(30) VALUE "  Grace Hopper".
       01  WS-FINE             PIC S9(5)V99 VALUE 12,50.
       01  WS-FINE-OUT         PIC ZZ.ZZ9,99.
       PROCEDURE DIVISION.
           MOVE WS-FINE TO WS-FINE-OUT
           DISPLAY UPPER-CASE(TRIM(WS-NAME)) " owes " WS-FINE-OUT
           STOP RUN.
       END PROGRAM FINEFMT.
GRACE HOPPER owes     12,50

Source: GnuCOBOL Manual

REPOSITORY

The REPOSITORY paragraph declares the names of the classes, interfaces, user-defined functions and (in COBOL 2014 and later) intrinsic functions that the program refers to, optionally mapping each to an external name. In the program above, FUNCTION UPPER-CASE TRIM INTRINSIC. lets the source use UPPER-CASE(...) and TRIM(...) without the FUNCTION keyword. Without a REPOSITORY entry (or the FUNCTION ALL INTRINSIC form) every call to an intrinsic function must be written with FUNCTION, as in FUNCTION UPPER-CASE(WS-NAME). Enterprise COBOL uses REPOSITORY for classes and for FUNCTION-ID user-defined functions (6.4 and later); see the intrinsic functions page for the function side. The exact list syntax is compiler specific in GnuCOBOL: the names are listed after one FUNCTION keyword and the list ends with INTRINSIC, and cobc 3.2 reports expecting intrinsic function name or INTRINSIC when it is left out.

The INPUT-OUTPUT SECTION

FILE-CONTROL has one SELECT entry per file, tying the program’s file name to a physical file and fixing its organization and access mode; I-O-CONTROL holds rarely used entries such as SAME AREA. The entries are the subject of File Concepts, SELECT and FD. The program above shows the shape: SELECT BOOK-FILE ASSIGN TO "BOOKMAST" ORGANIZATION IS LINE SEQUENTIAL FILE STATUS IS WS-BOOK-STATUS.

The DATA DIVISION and the PROCEDURE DIVISION

The data division has up to six sections, in this order when present: FILE SECTION (record layouts of the FD and SD entries), WORKING-STORAGE SECTION (data allocated once for the run unit), LOCAL-STORAGE SECTION (data allocated afresh on each invocation), LINKAGE SECTION (the parameters a program receives, and data it addresses), plus the COMMUNICATION SECTION and REPORT SECTION / SCREEN SECTION where the compiler supports them. A copybook such as BOOKREC is pulled in with COPY (see Copybooks and REPLACE).

The procedure division holds the executable logic. A main program normally has PROCEDURE DIVISION.. A program that receives parameters adds USING (and RETURNING), as in PROCEDURE DIVISION USING LK-TOTAL. below. Execution begins at the first paragraph or section and runs on until a statement stops it or control falls off the end, so every program ends explicitly, as the last section of this page shows. In the Circulation program above, MAIN-LINE SECTION holds MAIN-PARA, and SHOW-BOOK is a second paragraph that PERFORM SHOW-BOOK runs. Put loops and tests in separate paragraphs and call them with PERFORM; the structured style is on PERFORM and Structured Programming.

A period ends a sentence and, with it, every statement scope still open. Writing scope terminators (END-IF, END-READ, END-PERFORM) and putting a single period at the end of the paragraph, as the examples do, avoids the classic bug in which a stray period closes an IF early.

Nested programs, COMMON and several programs in one file

Besides separate source files, COBOL offers a way to keep a small private helper next to its user. A nested program is a complete program (all four divisions) written inside another program, between its procedure division and its END PROGRAM. A source file may also contain several programs one after another (a batch compile). Both forms need END PROGRAM name. so the compiler knows where each one stops.

The names are the first rule. A nested program can be called with CALL "name" only by the program that directly contains it, unless it is declared COMMON, in which case any program inside the same container can call it, including its siblings. A program cannot call a program that contains it. The second rule is the data: names declared in a program are local to it, so two programs can each have WS-CALLS, and a nested program can see a name of its container only if that name is declared GLOBAL. The program below has an outer NIGHTLY program and three nested programs:

  • COUNTER keeps its data between calls (the default).

  • FRESH is INITIAL, so its data is reset on every call.

  • SHOWER is COMMON, and is called by the siblings COUNTER and FRESH, not by NIGHTLY.

COUNTER and FRESH end differently on purpose: EXIT PROGRAM and GOBACK, which the next section compares.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. NIGHTLY.
      * Outer program with two nested programs.
       PROCEDURE DIVISION.
       MAIN-PARA.
           CALL "COUNTER"
           CALL "COUNTER"
           CALL "FRESH"
           CALL "FRESH"
           STOP RUN.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. COUNTER.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-CALLS            PIC 9(4) VALUE ZERO.
       PROCEDURE DIVISION.
           ADD 1 TO WS-CALLS
           CALL "SHOWER" USING WS-CALLS
           EXIT PROGRAM.
       END PROGRAM COUNTER.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. FRESH IS INITIAL.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-CALLS            PIC 9(4) VALUE ZERO.
       PROCEDURE DIVISION.
           ADD 1 TO WS-CALLS
           CALL "SHOWER" USING WS-CALLS
           GOBACK.
       END PROGRAM FRESH.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. SHOWER IS COMMON.
       DATA DIVISION.
       LINKAGE SECTION.
       01  LK-TOTAL            PIC 9(4).
       PROCEDURE DIVISION USING LK-TOTAL.
           DISPLAY "calls so far: " LK-TOTAL
           GOBACK.
       END PROGRAM SHOWER.

       END PROGRAM NIGHTLY.
calls so far: 0001
calls so far: 0002
calls so far: 0001
calls so far: 0001

COUNTER counts 1 and 2 because its WS-CALLS lives for the whole run unit. FRESH prints 1 twice, because INITIAL gives it a new copy each time. SHOWER is reached through a COMMON attribute, from two siblings that it does not belong to.

Nested and multiple programs have practical limits you should know:

  • The END PROGRAM header is required whenever a file holds more than one program. Leaving it out is an error in GnuCOBOL:

    noend.cob:11: error: multiple PROGRAM-ID's without matching END PROGRAM

    That file was deliberately wrong: two programs, FIRST and SECOND, with no END PROGRAM lines.

  • Which program of a multi-program file runs first depends on how the file is built; with cobc -x it is the first one. The build options are on Getting Started.

  • Nested programs share the compilation but not the data, and they cannot be replaced independently. Real systems keep each reusable program in its own source and call it by name (a "dynamic call", see Subprograms, CALL and Linkage); nesting is useful for small private helpers.

  • Enterprise COBOL supports nested programs and batch compilation (several outermost programs in one compilation unit), and enforces the rule that RECURSIVE appears only on an outermost program.

STOP RUN, GOBACK and EXIT PROGRAM

Three statements end a program, and picking the wrong one is a classic bug in called programs. The IBM documentation gives this behaviour (the Language Reference tables for STOP, GOBACK and EXIT):

Statement In a main program In a called subprogram

STOP RUN

Ends the run unit, closes every file of every program in it, and returns to the caller of the run unit (the system, which ends the application). The RETURN-CODE special register (modulo 4096) becomes the return code.

Ends the whole run unit, not just the subprogram. Control goes to the program that called the main program.

GOBACK

Works like STOP RUN: control returns to the system.

Works like EXIT PROGRAM: control returns to the caller. In a program with the INITIAL attribute it also acts as a CANCEL of that program.

EXIT PROGRAM

Does not end a main program: control continues with the next statement. Do not use it there.

Returns to the statement after the CALL.

The practical rule: use GOBACK in every program, because it does the right thing whether the program was started as the main program or called as a subprogram. Use STOP RUN only in the main program when you want it to be obvious that the program ends the run, and never in a subprogram that other programs call. EXIT PROGRAM belongs only in called programs. The next program shows what STOP RUN does in a subprogram. DRIVER calls QUITTER, which ends the run unit, so the message after the CALL is never shown:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. DRIVER.
       PROCEDURE DIVISION.
           CALL "QUITTER"
           DISPLAY "DRIVER: back from QUITTER"
           STOP RUN.
       END PROGRAM DRIVER.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. QUITTER.
       PROCEDURE DIVISION.
           DISPLAY "QUITTER: about to STOP RUN"
           STOP RUN.
       END PROGRAM QUITTER.
QUITTER: about to STOP RUN

A main program that returns a status to the job passes it through RETURN-CODE before it ends (MOVE 8 TO RETURN-CODE then GOBACK); the job control language (JCL) on z/OS tests it as the step condition code. The error handling page covers that convention.

Compiler differences and where the book is outdated

  • Standard. The four-division structure, PROGRAM-ID, nested programs, COMMON, INITIAL and RECURSIVE are in the standard since COBOL 85 (RECURSIVE since COBOL 2002). REPOSITORY and the FUNCTION ... INTRINSIC entries come from COBOL 2002. Free format (no columns) is also from COBOL 2002. The 2014 and 2023 editions leave the program structure the same.

  • Enterprise COBOL for z/OS 6.5. It supports nested programs and END PROGRAM, REPOSITORY for classes and user-defined functions, and keeps the obsolete identification paragraphs for compatibility. Enterprise COBOL 6.4 added FUNCTION-ID user-defined functions.

  • GnuCOBOL 3.2. It accepts the structure above, warns about obsolete paragraphs unless -std=ibm is used, requires END PROGRAM for multiple programs, and does not fully implement CURRENCY SIGN ... WITH PICTURE SYMBOL. Use -std=ibm to check that a program stays within IBM syntax.

  • The book. Taulli (Modern Mainframe Development, 2022) introduces the four divisions as a fixed template and leans on Enterprise COBOL 6.3 as the newest compiler. The structure it describes is still correct, but the compiler is two releases behind: 6.5 is current, and the standard it cites stops at COBOL 2002 although 2014 and 2023 were published. Its PC examples were run with OpenCobolIDE, which is no longer maintained; compile with GnuCOBOL 3.2 instead.