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:
- Its logical name is
CUSTOMER-MASTER-FILE-- descriptive and self-documenting. - It is assigned to the DD name
CUSTMAST, which the JCL will map to a physical VSAM dataset. - Its organization is
INDEXED, meaning records are accessed by a key value. - 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. - The record key is
CM-ACCOUNT-NUMBER, defined in the FILE SECTION. - 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:
- The transaction data was read into
DAILY-TRANSACTION-RECORD, whose layout was defined in the FILE SECTION of the DATA DIVISION. - The threshold
WS-HIGH-VALUE-THRESHOLDwas defined and initialized in WORKING-STORAGE. - The exception record is written to
EXCEPTION-FILE, whose physical assignment was established in the ENVIRONMENT DIVISION. - The entire check is orchestrated by paragraph
2200-CHECK-EXCEPTIONSin 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
-
The
0000-MAINparagraph inCUSTDLYcontains 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? -
The ENVIRONMENT DIVISION assigns the customer master file to the DD name
CUSTMASTrather 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? -
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? -
The program uses a read-ahead pattern:
8000-READ-TRANSACTIONis 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? -
Consider the fixed-format column rules. In
CUSTDLY, paragraph names like0000-MAINstart in column 8 (Area A), while statements likePERFORM 1000-INITIALIZEstart in column 12 (Area B). If you accidentally placed the paragraph name2000-PROCESS-TRANSACTIONSstarting in column 12, what would the compiler do? Would it treat the name as a statement, a data item, or something else? -
The
2200-CHECK-EXCEPTIONSparagraph 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. -
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.
-
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?