20 min read

> "Well-organized WORKING-STORAGE is the foundation of a well-organized program. A programmer who takes care with data definitions will take care with everything else."

Chapter 4: The WORKING-STORAGE and LOCAL-STORAGE Sections

"Well-organized WORKING-STORAGE is the foundation of a well-organized program. A programmer who takes care with data definitions will take care with everything else." -- Attributed to various senior COBOL architects

Introduction

Every COBOL program needs a place to store its working data -- the intermediate results of calculations, the flags that control program flow, the counters that track how many records have been processed, and the formatted fields that appear on reports. That place is the WORKING-STORAGE SECTION, and learning to use it well is one of the most important skills a COBOL programmer can develop.

In Chapter 3, you learned about the DATA DIVISION and its overall structure. Now we dive deep into the two storage sections that hold a program's internal data: WORKING-STORAGE and LOCAL-STORAGE. By the end of this chapter, you will understand how to define data items at every level number, initialize them properly, redefine storage for multiple interpretations, and organize your data definitions in a way that other programmers will thank you for.

The WORKING-STORAGE SECTION: Your Program's Data Area

The WORKING-STORAGE SECTION is where you define all the data items your program needs internally -- data that is not part of a file record and not passed as a parameter. It appears in the DATA DIVISION after any FILE SECTION:

       DATA DIVISION.
       FILE SECTION.
       ...
       WORKING-STORAGE SECTION.
       ...

What Belongs in WORKING-STORAGE

Think of WORKING-STORAGE as your program's scratchpad, toolbox, and filing cabinet combined:

  • Constants -- values that never change during execution (tax rates, maximum limits, program identifiers)
  • Flags and switches -- single-character fields that control program logic ("are we at end of file?")
  • Counters -- numeric fields that count events (records read, errors found)
  • Accumulators -- numeric fields that accumulate running totals (total sales, total tax)
  • Work fields -- intermediate calculation results
  • Record layouts -- structures that mirror input/output records
  • Display fields -- formatted fields for screen or report output
  • Date and time fields -- current date, formatted dates, date components
  • Table data -- arrays and lookup tables

Memory Allocation

When the program loads, COBOL allocates memory for every item defined in WORKING-STORAGE. The items exist for the entire life of the program (or until the program is canceled). Unlike LOCAL-STORAGE (discussed later in this chapter), WORKING-STORAGE items are allocated once and retain their values between calls if the program is called as a subprogram.

The total size of WORKING-STORAGE is the sum of all its elementary items. A group item's size is the sum of its subordinate elementary items. Understanding this is important -- COBOL does not add padding between fields unless you explicitly request alignment with the SYNCHRONIZED clause.

Organizing WORKING-STORAGE: A Professional Approach

Professional COBOL shops follow conventions for organizing WORKING-STORAGE. While the compiler does not enforce any particular order, a well-organized WORKING-STORAGE section is dramatically easier to maintain. Here is the recommended order:

1. Program Constants

      *----------------------------------------------------------------*
      * PROGRAM CONSTANTS
      *----------------------------------------------------------------*
       01  WS-C-PROGRAM-NAME     PIC X(20) VALUE 'PAYROLL-CALC'.
       01  WS-C-PROGRAM-VERSION  PIC X(05) VALUE '03.20'.
       01  WS-C-TAX-RATES.
           05  WS-C-FED-TAX-RATE PIC V9(4) VALUE 0.2200.
           05  WS-C-STATE-TAX-RT PIC V9(4) VALUE 0.0500.
           05  WS-C-FICA-RATE    PIC V9(4) VALUE 0.0765.
       01  WS-C-MAX-REG-HOURS    PIC 99    VALUE 40.

Constants go first because they set the context for the entire program. The WS-C- prefix makes them instantly recognizable. Grouping related constants (like tax rates) in a single 01-level group keeps them together.

2. Flags and Switches

      *----------------------------------------------------------------*
      * FLAGS AND SWITCHES
      *----------------------------------------------------------------*
       01  WS-F-EOF-FLAG         PIC X(01) VALUE 'N'.
           88  WS-EOF                      VALUE 'Y'.
           88  WS-NOT-EOF                  VALUE 'N'.
       01  WS-F-ERROR-FLAG       PIC X(01) VALUE 'N'.
           88  WS-ERROR-FOUND              VALUE 'Y'.
           88  WS-NO-ERROR                 VALUE 'N'.

Flags come next because they control the program's overall flow. Every flag should have 88-level condition names so the PROCEDURE DIVISION reads naturally.

3. Counters and Accumulators

      *----------------------------------------------------------------*
      * COUNTERS AND ACCUMULATORS
      *----------------------------------------------------------------*
       01  WS-CTR-RECORDS-READ   PIC 9(05) VALUE ZERO.
       01  WS-CTR-RECORDS-PROC   PIC 9(05) VALUE ZERO.
       01  WS-ACC-TOTAL-GROSS    PIC 9(09)V99 VALUE ZERO.
       01  WS-ACC-TOTAL-NET      PIC 9(09)V99 VALUE ZERO.

4. Work Fields

      *----------------------------------------------------------------*
      * WORK FIELDS
      *----------------------------------------------------------------*
       01  WS-WK-GROSS-PAY       PIC 9(07)V99 VALUE ZERO.
       01  WS-WK-NET-PAY         PIC 9(07)V99 VALUE ZERO.

5. Input/Output Record Layouts

      *----------------------------------------------------------------*
      * INPUT RECORD LAYOUT
      *----------------------------------------------------------------*
       01  WS-EMPLOYEE-RECORD.
           05  WS-EMP-ID         PIC X(06).
           05  WS-EMP-NAME       PIC X(30).
           05  WS-EMP-SALARY     PIC 9(07)V99.

6. Display and Report Fields

      *----------------------------------------------------------------*
      * DISPLAY FIELDS
      *----------------------------------------------------------------*
       01  WS-DSP-DETAIL-LINE.
           05  WS-DSP-EMP-ID     PIC X(06).
           05  FILLER             PIC X(02) VALUE SPACES.
           05  WS-DSP-EMP-NAME   PIC X(30).
           05  FILLER             PIC X(02) VALUE SPACES.
           05  WS-DSP-SALARY     PIC $$$,$$$,MATH1$,$$$,$$9.99.
           05  FILLER             PIC X(03) VALUE SPACES.
           05  WS-PRT-STATUS      PIC X(10).

Labels in Header Lines

       01  WS-HEADER-LINE.
           05  FILLER PIC X(05) VALUE SPACES.
           05  FILLER PIC X(20) VALUE 'EMPLOYEE NAME'.
           05  FILLER PIC X(03) VALUE SPACES.
           05  FILLER PIC X(14) VALUE 'SALARY'.
           05  FILLER PIC X(03) VALUE SPACES.
           05  FILLER PIC X(10) VALUE 'STATUS'.

Skipping Past Fields in REDEFINES

       01  WS-DEPOSIT-DATA REDEFINES WS-TRANSACTION-RECORD.
           05  FILLER             PIC X(10).    *> Skip header
           05  WS-DEP-ACCOUNT    PIC X(10).
           05  WS-DEP-AMOUNT     PIC 9(09)V99.

Padding Records to a Required Length

       01  WS-FIXED-80-RECORD.
           05  WS-DATA-FIELDS    PIC X(65).
           05  FILLER             PIC X(15).    *> Pad to 80

COBOL 2002 and Unnamed Items

In COBOL 2002 and later, you can omit the name entirely:

       01  WS-HEADER-LINE.
           05  PIC X(20) VALUE 'EMPLOYEE NAME'.
           05  PIC X(03) VALUE SPACES.
           05  PIC X(14) VALUE 'SALARY'.

However, most shops still prefer the explicit FILLER keyword for clarity. Multiple FILLER items in the same record are perfectly legal -- COBOL allows duplicate FILLER names precisely because they are never referenced individually.

Common Data Definition Patterns

Date Fields

       01  WS-DATE-YYYYMMDD      PIC 9(08).
       01  WS-DATE-PARTS REDEFINES WS-DATE-YYYYMMDD.
           05  WS-DATE-YYYY      PIC 9(04).
           05  WS-DATE-MM        PIC 9(02).
               88  WS-DATE-VALID-MONTH VALUE 01 THRU 12.
           05  WS-DATE-DD        PIC 9(02).
               88  WS-DATE-VALID-DAY   VALUE 01 THRU 31.
       01  WS-DATE-FORMATTED     PIC X(10).

Amount Fields

       01  WS-AMOUNT-WORK        PIC S9(11)V99 VALUE ZERO.
       01  WS-AMOUNT-DISPLAY     PIC -$$$,$$$,$$$,$$9.99.

Use signed fields (S) for amounts that can be negative. Make numeric-edited fields large enough for the formatted output.

Flag Fields

       01  WS-F-PROCESS-FLAG     PIC X(01) VALUE 'N'.
           88  WS-SHOULD-PROCESS          VALUE 'Y'.
           88  WS-SHOULD-NOT-PROCESS      VALUE 'N'.

Accumulators with Corresponding Display

       01  WS-ACC-TOTAL-SALES    PIC 9(13)V99 VALUE ZERO.
       01  WS-DSP-TOTAL-SALES    PIC $,MATH5$,$$$,$$9.99.

Fixed-Format vs. Free-Format Examples

All examples in this chapter use fixed-format (traditional columnar) COBOL. Here is how the same WORKING-STORAGE looks in free format, available in COBOL 2002+ and GnuCOBOL with the -free flag:

Fixed Format (Traditional)

       WORKING-STORAGE SECTION.
       01  WS-CUSTOMER-RECORD.
           05  WS-CUST-ID         PIC X(08).
           05  WS-CUST-NAME       PIC X(25).
           05  WS-CUST-BALANCE    PIC S9(07)V99.
           05  WS-CUST-STATUS     PIC X(01).
               88  WS-CUST-ACTIVE     VALUE 'A'.
               88  WS-CUST-INACTIVE   VALUE 'I'.

Free Format

WORKING-STORAGE SECTION.
01  WS-CUSTOMER-RECORD.
    05  WS-CUST-ID         PIC X(08).
    05  WS-CUST-NAME       PIC X(25).
    05  WS-CUST-BALANCE    PIC S9(07)V99.
    05  WS-CUST-STATUS     PIC X(01).
        88  WS-CUST-ACTIVE     VALUE 'A'.
        88  WS-CUST-INACTIVE   VALUE 'I'.

In free format: - No column restrictions (code can start in any column) - Comments use *> instead of * in column 7 - Line continuations are not needed as frequently - Indentation is by convention, not by column rule

The data definitions themselves are identical -- only the formatting changes. Throughout this textbook, we primarily use fixed format because it remains the dominant format in production COBOL systems.

Best Practices for WORKING-STORAGE Organization

Do not scatter related fields throughout WORKING-STORAGE. Keep all customer fields together, all calculation fields together, all display fields together.

2. Use Comment Blocks as Section Headers

      *----------------------------------------------------------------*
      * SECTION 1: PROGRAM CONSTANTS
      *----------------------------------------------------------------*

These visual separators make it easy to navigate a long WORKING-STORAGE section.

3. Initialize Everything

Every data item should have either a VALUE clause or be part of an INITIALIZE statement before first use. Uninitialized data is a common source of bugs.

4. Size Numeric Fields Generously

If a counter might eventually reach 10,000, define it as PIC 9(07), not PIC 9(04). The extra three bytes of storage are trivial compared to the cost of a production failure when the field overflows.

5. Use 88-Levels for Every Flag

Never compare a flag field directly to a literal in the PROCEDURE DIVISION. Always use 88-level condition names.

6. Keep WORKING-STORAGE DRY (Don't Repeat Yourself)

If the same constant appears in multiple places, define it once in the constants section and reference it by name.

7. Document Non-Obvious Fields

       01  WS-C-FICA-RATE    PIC V9(4) VALUE 0.0765.
      *    Social Security 6.2% + Medicare 1.45% = 7.65%

8. Make Display Fields Match Their Data Fields

If WS-WK-GROSS-PAY is PIC 9(07)V99, make sure WS-DSP-GROSS-PAY can display the maximum value: PIC $$$,$$$,$$9.99 (not PIC $$,$$9.99 which would truncate large values).

9. Prefix Consistently

Whatever prefix convention you choose, apply it consistently. Mixed conventions make code harder to navigate.

10. Avoid Excessive Nesting

While COBOL supports nesting up to 49 levels deep, practical data definitions rarely need more than 4-5 levels. Excessive nesting makes the code harder to read and the offsets harder to calculate mentally.

See Example 6 (example-06-complex-structures.cob) for a deeply nested structure modeling a complete bank account record, demonstrating how to manage complexity with proper naming and organization.

Common Pitfalls and How to Avoid Them

Pitfall 1: MOVE SPACES to a Numeric Field

      * WRONG:
           MOVE SPACES TO WS-MIXED-RECORD
      * If WS-MIXED-RECORD has numeric subordinates, they now
      * contain spaces. Any arithmetic causes an abend.

      * RIGHT:
           INITIALIZE WS-MIXED-RECORD

Pitfall 2: REDEFINES Size Mismatch

      * DANGEROUS:
       01  WS-SHORT-FIELD    PIC X(05).
       01  WS-LONG-VIEW REDEFINES WS-SHORT-FIELD.
           05  WS-PART-A     PIC X(05).
           05  WS-PART-B     PIC X(05).    *> Extends past original!

WS-PART-B references memory beyond WS-SHORT-FIELD, potentially corrupting adjacent data.

Pitfall 3: Forgetting to Initialize Before a Loop

      * WRONG:
           PERFORM UNTIL WS-EOF
               READ INPUT-FILE
               ADD WS-INP-AMOUNT TO WS-TOTAL
      *        WS-WK-TAX is never reset between records!
               COMPUTE WS-WK-TAX = WS-INP-AMOUNT * WS-TAX-RATE
               ADD WS-WK-TAX TO WS-TOTAL-TAX
           END-PERFORM

      * RIGHT:
           PERFORM UNTIL WS-EOF
               READ INPUT-FILE
               INITIALIZE WS-WK-CALCULATION-FIELDS
               ADD WS-INP-AMOUNT TO WS-TOTAL
               COMPUTE WS-WK-TAX = WS-INP-AMOUNT * WS-TAX-RATE
               ADD WS-WK-TAX TO WS-TOTAL-TAX
           END-PERFORM

Pitfall 4: Numeric Field Overflow Without Detection

       01  WS-COUNTER   PIC 9(03) VALUE ZERO.
      * If this counter exceeds 999, it silently wraps to 000.
      * Use ON SIZE ERROR to catch this:
           ADD 1 TO WS-COUNTER
               ON SIZE ERROR
                   DISPLAY 'Counter overflow!'
           END-ADD

Pitfall 5: Using GROUP-Level Comparisons on Numeric Data

       01  WS-DATE.
           05  WS-YEAR  PIC 9(04).
           05  WS-MONTH PIC 9(02).
           05  WS-DAY   PIC 9(02).

      * This is an alphanumeric comparison (group item):
           IF WS-DATE > '20240101'       *> Works but uses alpha sort

      * This is clearer and more portable:
           IF WS-YEAR > 2024
               OR (WS-YEAR = 2024 AND WS-MONTH > 01)

Chapter Summary

This chapter covered the critical topic of defining and organizing data in COBOL programs:

  • WORKING-STORAGE is the primary section for a program's internal data, allocated once and persistent for the program's lifetime
  • Organizational conventions (constants, flags, counters, work fields, display fields) make programs maintainable
  • Level numbers define hierarchical structures: 01 (record), 02-49 (subordinate), 66 (RENAMES), 77 (independent), 88 (conditions)
  • The VALUE clause initializes data at load time, with figurative constants (SPACES, ZEROS, HIGH-VALUES) providing common defaults
  • INITIALIZE is the safest way to reset mixed-type records at runtime, setting alphanumeric to spaces and numeric to zeros
  • REDEFINES allows multiple interpretations of the same storage area, essential for date handling, multi-format records, validation, and table initialization
  • 88-level condition names are fundamental to readable COBOL -- use them for every flag, status code, and enumerated value
  • OCCURS defines repeating data items (tables), accessible by subscript
  • LOCAL-STORAGE provides per-invocation initialization, essential for recursive and reentrant programs
  • FILLER items provide spacing, labels, and padding within records
  • SYNCHRONIZED requests boundary alignment for binary fields
  • Consistent naming conventions with meaningful prefixes make large programs navigable
  • INITIALIZE is safer than MOVE SPACES for mixed-type records

In the next chapter, we will examine the PROCEDURE DIVISION and learn to write the executable logic that manipulates all the data we have now learned to define.