Case Study 2: Analyzing a Real-World COBOL Program Structure


Background

Continental Savings Bank (CSB) is a regional bank headquartered in Richmond, Virginia, serving approximately 340,000 retail and commercial customers across 45 branches. The bank's nightly batch processing cycle includes a program called CUSTDLY -- the Customer Daily Activity Summary program. This program reads the customer master file and the daily transaction file, produces a formatted summary report of account activity for branch managers, and writes exception records for accounts that have triggered review thresholds.

CUSTDLY was originally written in 2014 by senior developer Raymond Ito as a replacement for an older, undocumented program that had become unmaintainable. Raymond designed the program with clarity and future maintenance as his primary goals. Today, CUSTDLY serves as the bank's internal example of a well-structured COBOL program. New developers at CSB study it during their first week on the job.

This case study presents the complete source code of CUSTDLY, walks through each of its four divisions, and analyzes the structural decisions that make it readable and maintainable. The program is approximately 190 lines of fixed-format COBOL and is fully compilable with GnuCOBOL or IBM Enterprise COBOL.


The Complete Program

The following listing shows the entire CUSTDLY program. Read through it once to get a general sense of the structure, then we will examine each division in detail.

      *================================================================*
      * Program:     CUSTDLY
      * Description: Customer Daily Activity Summary Report
      * Author:      Raymond Ito
      * Date:        2024-03-15
      * Called by:    JCL job NIGHTCYC step STEP045
      * Calls:       None
      * Files:       CUSTMAST - Customer master file (input)
      *              DAYTRANS - Daily transaction file (input)
      *              RPTOUT   - Activity summary report (output)
      *              EXCEPTFL - Exception records (output)
      * Modification History:
      *   Date       Author     Description
      *   ---------- ---------- ----------------------------------
      *   2024-03-15 R. Ito     Initial development
      *   2024-09-02 R. Ito     Added dormant account detection
      *   2025-06-11 P. Okafor  Added high-value transaction flag
      *================================================================*
       IDENTIFICATION DIVISION.
       PROGRAM-ID.    CUSTDLY.
       AUTHOR.        RAYMOND ITO.
       DATE-WRITTEN.  2024-03-15.
       INSTALLATION.  CONTINENTAL SAVINGS BANK - NIGHTLY BATCH.

       ENVIRONMENT DIVISION.

       CONFIGURATION SECTION.
       SOURCE-COMPUTER. IBM-ZOS.
       OBJECT-COMPUTER. IBM-ZOS.
       SPECIAL-NAMES.
           CURRENCY SIGN IS "$".

       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT CUSTOMER-MASTER-FILE
               ASSIGN TO CUSTMAST
               ORGANIZATION IS INDEXED
               ACCESS MODE IS RANDOM
               RECORD KEY IS CM-ACCOUNT-NUMBER
               FILE STATUS IS WS-CUST-STATUS.

           SELECT DAILY-TRANSACTION-FILE
               ASSIGN TO DAYTRANS
               ORGANIZATION IS SEQUENTIAL
               FILE STATUS IS WS-TRAN-STATUS.

           SELECT ACTIVITY-REPORT-FILE
               ASSIGN TO RPTOUT
               ORGANIZATION IS LINE SEQUENTIAL
               FILE STATUS IS WS-RPT-STATUS.

           SELECT EXCEPTION-FILE
               ASSIGN TO EXCEPTFL
               ORGANIZATION IS SEQUENTIAL
               FILE STATUS IS WS-EXCP-STATUS.

       DATA DIVISION.

       FILE SECTION.

       FD  CUSTOMER-MASTER-FILE
           RECORD CONTAINS 120 CHARACTERS.
       01  CUSTOMER-MASTER-RECORD.
           05  CM-ACCOUNT-NUMBER    PIC 9(10).
           05  CM-CUSTOMER-NAME     PIC X(30).
           05  CM-ACCOUNT-TYPE      PIC X.
               88  CM-CHECKING      VALUE 'C'.
               88  CM-SAVINGS       VALUE 'S'.
               88  CM-MONEY-MARKET  VALUE 'M'.
           05  CM-CURRENT-BALANCE   PIC S9(9)V99.
           05  CM-DATE-OPENED       PIC 9(8).
           05  CM-LAST-ACTIVITY     PIC 9(8).
           05  CM-BRANCH-CODE       PIC 9(3).
           05  CM-STATUS-CODE       PIC X.
               88  CM-ACTIVE        VALUE 'A'.
               88  CM-FROZEN        VALUE 'F'.
               88  CM-CLOSED        VALUE 'X'.
           05  FILLER               PIC X(47).

       FD  DAILY-TRANSACTION-FILE
           RECORD CONTAINS 80 CHARACTERS.
       01  DAILY-TRANSACTION-RECORD.
           05  DT-ACCOUNT-NUMBER    PIC 9(10).
           05  DT-TRANS-TYPE        PIC X(2).
               88  DT-DEPOSIT       VALUE 'DP'.
               88  DT-WITHDRAWAL    VALUE 'WD'.
               88  DT-TRANSFER-IN   VALUE 'TI'.
               88  DT-TRANSFER-OUT  VALUE 'TO'.
               88  DT-FEE           VALUE 'FE'.
           05  DT-TRANS-AMOUNT      PIC S9(7)V99.
           05  DT-TRANS-DATE        PIC 9(8).
           05  DT-TRANS-TIME        PIC 9(6).
           05  DT-BRANCH-ORIGIN     PIC 9(3).
           05  DT-TELLER-ID         PIC X(6).
           05  FILLER               PIC X(28).

       FD  ACTIVITY-REPORT-FILE
           RECORD CONTAINS 132 CHARACTERS.
       01  REPORT-LINE              PIC X(132).

       FD  EXCEPTION-FILE
           RECORD CONTAINS 100 CHARACTERS.
       01  EXCEPTION-RECORD.
           05  EX-ACCOUNT-NUMBER    PIC 9(10).
           05  EX-EXCEPTION-CODE    PIC X(2).
           05  EX-TRANS-AMOUNT      PIC S9(7)V99.
           05  EX-CURRENT-BALANCE   PIC S9(9)V99.
           05  EX-DESCRIPTION       PIC X(50).
           05  EX-DATE-DETECTED     PIC 9(8).
           05  FILLER               PIC X(9).

       WORKING-STORAGE SECTION.

      *--- Program Constants ---
       01  WS-PROGRAM-NAME          PIC X(7)  VALUE 'CUSTDLY'.
       01  WS-HIGH-VALUE-THRESHOLD  PIC 9(7)V99
                                    VALUE 10000.00.
       01  WS-DORMANT-DAYS          PIC 9(3)  VALUE 180.

      *--- File Status Variables ---
       01  WS-FILE-STATUSES.
           05  WS-CUST-STATUS       PIC XX    VALUE SPACES.
           05  WS-TRAN-STATUS       PIC XX    VALUE SPACES.
           05  WS-RPT-STATUS        PIC XX    VALUE SPACES.
           05  WS-EXCP-STATUS       PIC XX    VALUE SPACES.

      *--- Processing Flags ---
       01  WS-FLAGS.
           05  WS-TRAN-EOF-FLAG     PIC X     VALUE 'N'.
               88  TRAN-END-OF-FILE VALUE 'Y'.
               88  TRAN-NOT-EOF     VALUE 'N'.
           05  WS-CUST-FOUND-FLAG   PIC X     VALUE 'N'.
               88  CUSTOMER-FOUND   VALUE 'Y'.
               88  CUSTOMER-MISSING VALUE 'N'.

      *--- Counters and Accumulators ---
       01  WS-COUNTERS.
           05  WS-TRANS-READ        PIC 9(7)  VALUE ZEROS.
           05  WS-TRANS-PROCESSED   PIC 9(7)  VALUE ZEROS.
           05  WS-EXCEPTIONS-WRITTEN PIC 9(5) VALUE ZEROS.
           05  WS-REPORT-LINES      PIC 9(5)  VALUE ZEROS.
           05  WS-CUST-NOT-FOUND    PIC 9(5)  VALUE ZEROS.

      *--- Date Fields ---
       01  WS-CURRENT-DATE-FIELDS.
           05  WS-CURRENT-DATE      PIC 9(8)  VALUE ZEROS.
           05  WS-CURRENT-DATE-R REDEFINES WS-CURRENT-DATE.
               10  WS-CURR-YEAR    PIC 9(4).
               10  WS-CURR-MONTH   PIC 9(2).
               10  WS-CURR-DAY     PIC 9(2).
           05  WS-CURRENT-TIME      PIC 9(6)  VALUE ZEROS.

      *--- Report Header Line ---
       01  WS-RPT-HEADER.
           05  FILLER               PIC X(5)  VALUE SPACES.
           05  FILLER               PIC X(42) VALUE
               'CONTINENTAL SAVINGS BANK - DAILY ACTIVITY'.
           05  FILLER               PIC X(10) VALUE SPACES.
           05  FILLER               PIC X(6)  VALUE 'DATE: '.
           05  WS-HDR-DATE          PIC X(10) VALUE SPACES.
           05  FILLER               PIC X(59) VALUE SPACES.

      *--- Report Detail Line ---
       01  WS-RPT-DETAIL.
           05  FILLER               PIC X(2)  VALUE SPACES.
           05  WS-DET-ACCOUNT       PIC 9(10).
           05  FILLER               PIC X(2)  VALUE SPACES.
           05  WS-DET-NAME          PIC X(30).
           05  FILLER               PIC X(1)  VALUE SPACES.
           05  WS-DET-TYPE          PIC X(3).
           05  FILLER               PIC X(1)  VALUE SPACES.
           05  WS-DET-TRANS-TYPE    PIC X(4).
           05  FILLER               PIC X(1)  VALUE SPACES.
           05  WS-DET-AMOUNT        PIC $$$,$$$,MATH1$,$$$,$$$,MATH3$,$$$,$$9.99-` in the report detail line. If CSB opened a European branch, a programmer could change the currency sign to the euro symbol without modifying any PICTURE clause in the DATA DIVISION or any logic in the PROCEDURE DIVISION -- an example of the separation of concerns that the ENVIRONMENT DIVISION provides.

#### INPUT-OUTPUT SECTION

```cobol
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT CUSTOMER-MASTER-FILE
               ASSIGN TO CUSTMAST
               ORGANIZATION IS INDEXED
               ACCESS MODE IS RANDOM
               RECORD KEY IS CM-ACCOUNT-NUMBER
               FILE STATUS IS WS-CUST-STATUS.

           SELECT DAILY-TRANSACTION-FILE
               ASSIGN TO DAYTRANS
               ORGANIZATION IS SEQUENTIAL
               FILE STATUS IS WS-TRAN-STATUS.

The FILE-CONTROL paragraph contains four SELECT statements, one for each file the program uses. Each SELECT maps a logical file name (used throughout the program) to a physical assignment (resolved by the operating system or JCL at runtime).

Consider the customer master file. The SELECT statement tells us five things about this file:

  1. Its logical name is CUSTOMER-MASTER-FILE -- descriptive and self-documenting.
  2. It is assigned to the DD name CUSTMAST, which the JCL will map to a physical VSAM dataset.
  3. Its organization is INDEXED, meaning records are accessed by a key value.
  4. The access mode is RANDOM, because the program looks up individual customer records by account number rather than reading the file sequentially from beginning to end.
  5. The record key is CM-ACCOUNT-NUMBER, defined in the FILE SECTION.
  6. A FILE STATUS variable (WS-CUST-STATUS) will receive a two-character status code after every I/O operation on this file.

The daily transaction file, by contrast, is SEQUENTIAL with no explicit access mode (which defaults to sequential). This file is read from beginning to end, one record at a time. The report file uses LINE SEQUENTIAL organization, which means each record is terminated by a line delimiter -- appropriate for a human-readable text report.

Every file has a FILE STATUS clause. This is a non-negotiable coding standard at CSB, and for good reason. Without FILE STATUS, a failed file operation can cause unpredictable behavior -- the program might continue processing with garbage data, or it might abend with an unhelpful system code. With FILE STATUS, the program can detect the error, display a meaningful message, and terminate gracefully.

The DATA DIVISION

The DATA DIVISION in CUSTDLY is the largest division, which is typical of COBOL programs. It contains two sections: FILE SECTION and WORKING-STORAGE SECTION.

FILE SECTION

The FILE SECTION defines the record layout for each of the four files. Let us examine the customer master record in detail:

       FD  CUSTOMER-MASTER-FILE
           RECORD CONTAINS 120 CHARACTERS.
       01  CUSTOMER-MASTER-RECORD.
           05  CM-ACCOUNT-NUMBER    PIC 9(10).
           05  CM-CUSTOMER-NAME     PIC X(30).
           05  CM-ACCOUNT-TYPE      PIC X.
               88  CM-CHECKING      VALUE 'C'.
               88  CM-SAVINGS       VALUE 'S'.
               88  CM-MONEY-MARKET  VALUE 'M'.

The FD (File Description) entry specifies that records in this file are 120 characters long. Beneath it, the 01 level record description breaks those 120 characters into named fields. The CM- prefix on every field name tells the reader that these fields belong to the Customer Master record. When you encounter CM-ACCOUNT-TYPE anywhere in the PROCEDURE DIVISION, you know immediately that it comes from the customer master file, not from a working-storage variable or the transaction file.

The 88-level condition names are one of COBOL's most readable features. Instead of writing IF CM-ACCOUNT-TYPE = 'C', the programmer writes IF CM-CHECKING. The code reads like a business specification rather than a technical instruction. Note that the transaction type field in the daily transaction record uses two-character codes ('DP', 'WD', 'TI', 'TO', 'FE'), each with a meaningful condition name. The EVALUATE TRUE block in paragraph 2100-FORMAT-DETAIL-LINE demonstrates how naturally these condition names integrate into the processing logic.

The FILLER items at the end of each record deserve attention. The customer master record has a 47-character FILLER, and the transaction record has a 28-character FILLER. These represent unused space in the physical record. When the file was originally designed, the architects left room for future fields. Over time, new fields like CM-LAST-ACTIVITY were added by consuming some of the original FILLER space. This is a standard practice in fixed-length record design: always reserve FILLER for future expansion.

WORKING-STORAGE SECTION

The WORKING-STORAGE SECTION in CUSTDLY is organized into clearly separated groups, each preceded by a comment banner:

*--- Program Constants ---
*--- File Status Variables ---
*--- Processing Flags ---
*--- Counters and Accumulators ---
*--- Date Fields ---
*--- Report Header Line ---
*--- Report Detail Line ---
*--- Report Summary Line ---

This organization is not accidental. It follows CSB's standard that every WORKING-STORAGE SECTION must arrange items in this order: constants first, then file status variables, then flags, then counters, then date fields, then work areas, then report lines. A developer looking for a file status variable does not need to scan the entire section -- they go directly to the second group.

The naming convention reinforces the organization. Every working-storage item starts with WS-. Within the report lines, header fields use WS-HDR-, detail fields use WS-DET-, and summary fields use WS-SUM-. This layered prefix system means that a data name alone tells you three things: that the item is in working storage, that it is part of a report line, and which report line it belongs to.

The date fields demonstrate the REDEFINES clause:

       01  WS-CURRENT-DATE-FIELDS.
           05  WS-CURRENT-DATE      PIC 9(8)  VALUE ZEROS.
           05  WS-CURRENT-DATE-R REDEFINES WS-CURRENT-DATE.
               10  WS-CURR-YEAR    PIC 9(4).
               10  WS-CURR-MONTH   PIC 9(2).
               10  WS-CURR-DAY     PIC 9(2).

The program accepts the system date as an 8-digit number (YYYYMMDD) into WS-CURRENT-DATE. The REDEFINES clause provides an alternative view of the same 8 bytes, breaking them into year, month, and day components. No data is copied or moved -- both names refer to the same memory. This is a common and efficient pattern for date handling in COBOL.

The PROCEDURE DIVISION

The PROCEDURE DIVISION is where the program comes alive. CUSTDLY uses a paragraph-only structure (no sections), following CSB's standard for programs under 500 lines. Larger programs at CSB use sections.

The Main Control Paragraph

       0000-MAIN.
           PERFORM 1000-INITIALIZE
           PERFORM 2000-PROCESS-TRANSACTIONS
               UNTIL TRAN-END-OF-FILE
           PERFORM 3000-WRITE-SUMMARY
           PERFORM 4000-FINALIZE
           STOP RUN
           .

This five-line paragraph is the entire orchestration of the program. It reads almost like an English outline: initialize, process transactions until the file is exhausted, write the summary, finalize, and stop. There is no business logic here, no file I/O, no data movement -- only control flow. A reader can understand the program's overall structure in five seconds.

The PERFORM 2000-PROCESS-TRANSACTIONS UNTIL TRAN-END-OF-FILE statement is the main processing loop. COBOL evaluates the condition TRAN-END-OF-FILE before each execution of the paragraph. When the condition is true (because 8000-READ-TRANSACTION set the flag), the loop terminates.

Notice the lone period on its own line at the end of the paragraph. This is CSB's standard: one period per paragraph, placed on a separate line to make paragraph boundaries visible.

The PERFORM Flow

The execution flow of CUSTDLY follows a clear hierarchy:

0000-MAIN
  |
  +-- 1000-INITIALIZE
  |     +-- 9000-CHECK-FILE-STATUS
  |     +-- 8000-READ-TRANSACTION (prime read)
  |
  +-- 2000-PROCESS-TRANSACTIONS (loop)
  |     +-- 2100-FORMAT-DETAIL-LINE
  |     +-- 2200-CHECK-EXCEPTIONS
  |     +-- 8000-READ-TRANSACTION
  |
  +-- 3000-WRITE-SUMMARY
  |
  +-- 4000-FINALIZE

Every paragraph is reached through a PERFORM from a higher-level paragraph. There are no GO TO statements. The flow is strictly top-down: the main paragraph calls four processing phases, and those phases call lower-level paragraphs as needed.

This structure embodies a principle called structured programming. Each paragraph has a single entry point (its name) and a single exit point (the end of the paragraph). Control flows in one direction -- downward through the PERFORM hierarchy and back up when each paragraph completes. There are no unexpected jumps, no backward branches, and no hidden paths.

The 8000-READ-TRANSACTION paragraph is called from two places: once in 1000-INITIALIZE (the "prime read" that loads the first record) and once at the end of 2000-PROCESS-TRANSACTIONS (to advance to the next record). This is the standard read-ahead pattern in COBOL batch processing. The prime read ensures that the main loop's UNTIL condition has a valid value to test before the first iteration.


Understanding the Fixed-Format Layout

Every line of CUSTDLY adheres to the fixed-format column rules. Let us examine a few lines to see these rules in action.

Column Layout in Practice

Consider this line from the IDENTIFICATION DIVISION:

Columns: 1234567890123456789012345678901234567...
         _______ IDENTIFICATION DIVISION.
         ||||||||
         123456789...
              ^col 7 (space = normal line)
               ^col 8 (Area A starts)

The first six columns (sequence number area) are blank. Column 7 is a space, indicating a normal source line. The text IDENTIFICATION DIVISION. begins in column 8 -- Area A -- because division headers must start in Area A.

Now consider a comment line:

Columns: 1234567890123456789012345678901234567...
         ______*==============================...
              ^col 7 (* = comment)

The asterisk in column 7 marks the entire line as a comment. The compiler ignores everything from column 8 through column 72.

A statement from the PROCEDURE DIVISION:

Columns: 1234567890123456789012345678901234567...
         ___________PERFORM 1000-INITIALIZE
                    ^col 12 (Area B starts)

The PERFORM statement begins in column 12 -- Area B -- because executable statements must appear in Area B. If this statement were accidentally placed starting in column 8, many compilers would interpret it as a paragraph name, which would cause a compilation error or, worse, unexpected behavior.

A paragraph name:

Columns: 1234567890123456789012345678901234567...
         _______0000-MAIN.
                ^col 8 (Area A)

The paragraph name 0000-MAIN. begins in column 8 -- Area A -- because paragraph names must start in Area A. This is how the compiler distinguishes paragraph names from statements.

The Column 72 Boundary

Columns 73-80 are the identification area, ignored by the compiler. Any text that extends past column 72 will be silently discarded. In CUSTDLY, Raymond was careful to keep all code within columns 8-72. The report header VALUE clause is a case where line length becomes a concern:

           05  FILLER               PIC X(42) VALUE
               'CONTINENTAL SAVINGS BANK - DAILY ACTIVITY'.

Rather than trying to fit the entire clause on one line (which would have exceeded column 72), Raymond broke the VALUE clause across two lines. The continuation of the string literal onto the next line uses standard COBOL line-break rules: the opening quote on the second line starts in Area B.


How the Four Divisions Work Together

The four divisions in CUSTDLY are not independent islands -- they form an integrated system where each division depends on and serves the others.

The IDENTIFICATION DIVISION establishes the program's identity. The name CUSTDLY is what the JCL uses to load and execute the program. If another program needed to call this one, it would issue CALL 'CUSTDLY'.

The ENVIRONMENT DIVISION creates the bridge between the program and the outside world. The four SELECT statements define the files the program will use, but the actual physical datasets are determined at runtime by the JCL. This means the same program can process test data or production data simply by changing the JCL -- no recompilation required. The SPECIAL-NAMES paragraph configures the currency symbol, which the DATA DIVISION's PICTURE clauses rely on.

The DATA DIVISION defines the structure of every piece of data the program touches. The FILE SECTION record layouts determine how the program interprets the bytes it reads from and writes to files. The WORKING-STORAGE SECTION provides the variables, counters, flags, and report templates that the PROCEDURE DIVISION needs. The PICTURE clauses in the report lines (like PIC $$$,$$$,$$9.99-) depend on the currency sign declared in the ENVIRONMENT DIVISION.

The PROCEDURE DIVISION implements the logic, operating exclusively on data items defined in the DATA DIVISION and performing I/O on files declared in the ENVIRONMENT DIVISION. When the PROCEDURE DIVISION executes READ CUSTOMER-MASTER-FILE, the ENVIRONMENT DIVISION determines which physical file is read, and the DATA DIVISION determines how the bytes in that record are interpreted.

Consider a concrete example of this integration. When the program encounters a high-value transaction:

  1. The transaction data was read into DAILY-TRANSACTION-RECORD, whose layout was defined in the FILE SECTION of the DATA DIVISION.
  2. The threshold WS-HIGH-VALUE-THRESHOLD was defined and initialized in WORKING-STORAGE.
  3. The exception record is written to EXCEPTION-FILE, whose physical assignment was established in the ENVIRONMENT DIVISION.
  4. The entire check is orchestrated by paragraph 2200-CHECK-EXCEPTIONS in the PROCEDURE DIVISION.

All four divisions participate in this single business operation.


Common Structural Mistakes and How to Avoid Them

Raymond Ito, who mentors junior developers at CSB, keeps a list of the structural mistakes he sees most often. Here are the most consequential ones, illustrated with examples from code he has reviewed.

Mistake 1: Putting Business Logic in the Main Paragraph

      * WRONG: Main paragraph does too much
       0000-MAIN.
           OPEN INPUT CUSTOMER-MASTER-FILE
           READ DAILY-TRANSACTION-FILE
           IF CM-ACCOUNT-TYPE = 'C'
               COMPUTE WS-FEE = WS-BALANCE * 0.005
           END-IF
           CLOSE CUSTOMER-MASTER-FILE
           STOP RUN.

The main control paragraph should contain nothing but PERFORM statements and STOP RUN. Mixing file operations, conditionals, and calculations into the main paragraph defeats the purpose of structured organization. A reader should be able to understand the program's overall flow by reading only the main paragraph.

Mistake 2: Neglecting the Prime Read

      * WRONG: No prime read -- the loop condition is tested
      *        before any record has been read
       0000-MAIN.
           PERFORM 1000-INITIALIZE
           PERFORM 2000-PROCESS UNTIL END-OF-FILE
           STOP RUN.

       1000-INITIALIZE.
           OPEN INPUT TRANSACTION-FILE.

       2000-PROCESS.
           READ TRANSACTION-FILE AT END SET END-OF-FILE TO TRUE
           END-READ
           ADD 1 TO WS-COUNT.

Without the prime read in 1000-INITIALIZE, the first iteration of the loop enters 2000-PROCESS with no record loaded. The ADD statement executes on uninitialized data. The correct pattern, as shown in CUSTDLY, is to perform the first READ inside the initialization paragraph so that data is ready before the loop begins.

Mistake 3: Inconsistent Naming Prefixes

      * WRONG: Mixed naming conventions
       01  ACCOUNT-NUMBER    PIC 9(10).
       01  WS-BALANCE        PIC S9(9)V99.
       01  BAL-TOTAL         PIC S9(11)V99.
       01  X                 PIC 9(3).

When a developer encounters ACCOUNT-NUMBER in the PROCEDURE DIVISION, they cannot tell whether it is in WORKING-STORAGE, the FILE SECTION, or the LINKAGE SECTION. Is X a counter, a flag, or a temporary variable? Consistent prefixes (WS-, CM-, DT-, EX-) eliminate this ambiguity entirely.

Mistake 4: Forgetting Scope Terminators

      * WRONG: Using periods inside conditional blocks
           IF WS-BALANCE > 0
               DISPLAY 'POSITIVE'.
               ADD 1 TO WS-POS-COUNT.
           DISPLAY 'DONE CHECKING'.

The period after 'POSITIVE' terminates the IF statement. The ADD statement executes unconditionally, regardless of the balance. The final DISPLAY also executes unconditionally. The indentation is misleading -- it suggests the ADD is inside the IF, but the period says otherwise. Always use END-IF (and other explicit scope terminators) and reserve the period for the very end of the paragraph.

Mistake 5: Omitting FILE STATUS Checks

      * WRONG: No status check -- if the file does not exist,
      *        the READ will fail unpredictably
           OPEN INPUT TRANSACTION-FILE
           READ TRANSACTION-FILE

If the file cannot be opened (because the DD name is missing from the JCL, or the dataset does not exist), the program's behavior depends on the runtime environment. It might abend with a cryptic system code, or it might continue processing with an empty buffer. A FILE STATUS check after every OPEN, READ, WRITE, and CLOSE operation makes the program's error handling explicit and predictable.

Mistake 6: Placing Level Numbers in the Wrong Area

      * WRONG: 01-level in Area B (column 12+)
           01  WS-COUNTER PIC 9(5).

      * WRONG: 05-level in Area A (column 8)
       05  WS-NAME PIC X(30).

Level numbers 01 and 77 must begin in Area A (columns 8-11). Level numbers 02 through 49, 66, and 88 must begin in Area B (columns 12-72). This is one of the most frequent errors for beginners who are accustomed to free-form languages. Most modern compilers will flag these violations, but some older compilers may silently misinterpret the intent.


What Makes This Program Maintainable

CUSTDLY has been in production for over a year and has been modified twice since its initial development. Both modifications -- adding dormant account detection and adding a high-value transaction flag -- were completed in under two days each, by a developer who did not write the original program. When asked why the modifications went so smoothly, the developer (P. Okafor, noted in the modification history) identified three structural factors:

Factor 1: The comment block at the top told her exactly what the program does, what files it uses, and where it fits in the nightly cycle. She did not need to read the entire program to understand its purpose.

Factor 2: The paragraph naming convention told her where to make changes. The high-value transaction flag was an exception check, so she looked at the 2200-series paragraphs. She found 2200-CHECK-EXCEPTIONS and added the new logic there. She did not need to search the entire PROCEDURE DIVISION.

Factor 3: The separation between the control flow (0000-MAIN) and the business logic (2000-series paragraphs) meant she could add new processing without disturbing the overall program structure. She added code to an existing paragraph rather than reorganizing the entire program.

These are not accidental benefits. They are the direct result of following a consistent structural standard across all four divisions.


Discussion Questions

  1. The 0000-MAIN paragraph in CUSTDLY contains only five statements: four PERFORMs and STOP RUN. Why is it important that the main paragraph contains no business logic? What problems would arise if the developer had placed the file-open logic and the date-formatting logic directly in the main paragraph?

  2. The ENVIRONMENT DIVISION assigns the customer master file to the DD name CUSTMAST rather than to a hard-coded file path. Explain how this separation between logical and physical file names benefits testing. How would you run this same program against a test dataset without modifying the source code?

  3. Examine the WORKING-STORAGE SECTION and identify every naming prefix used (e.g., WS-, CM-, DT-). For each prefix, explain what section or record the prefix identifies. Why is this prefixing convention more valuable in a 5,000-line program than in a 200-line program?

  4. The program uses a read-ahead pattern: 8000-READ-TRANSACTION is called once during initialization and again at the end of each iteration of the processing loop. Draw a diagram showing the sequence of events during the first three iterations. What would happen if the transaction file contained zero records?

  5. Consider the fixed-format column rules. In CUSTDLY, paragraph names like 0000-MAIN start in column 8 (Area A), while statements like PERFORM 1000-INITIALIZE start in column 12 (Area B). If you accidentally placed the paragraph name 2000-PROCESS-TRANSACTIONS starting in column 12, what would the compiler do? Would it treat the name as a statement, a data item, or something else?

  6. The 2200-CHECK-EXCEPTIONS paragraph contains two separate IF blocks rather than a combined condition using OR. Why might the developer have chosen this structure? Consider what happens when a transaction on a frozen account also exceeds the high-value threshold.

  7. The program modification history shows two changes by two different developers. If you were asked to add a third exception rule -- flagging any withdrawal that exceeds 80% of the account's current balance -- identify the specific paragraph where you would add the logic, the data items you would reference, and the steps you would take to test the change without affecting production data.

  8. Compare the FILE SECTION's record layouts for the transaction file (80 characters) and the customer master file (120 characters). Both have FILLER fields at the end. Explain why fixed-length records with reserved FILLER space are preferred over variable-length records in mainframe batch processing systems. What trade-off does this design involve?