Chapter 39 Exercises: Legacy System Maintenance and Modernization

These exercises are organized into five tiers of increasing difficulty. Work through them sequentially to build mastery, or jump to the tier that matches your current skill level.


Tier 1: Recall (Exercises 1-6)

These exercises cover the fundamental concepts and vocabulary of legacy code maintenance and modernization.

Exercise 1: Legacy Code Indicators

List five common characteristics of legacy COBOL code that indicate it needs modernization. For each characteristic, explain why it is problematic.

Solution:

  1. Excessive GO TO statements -- Creates spaghetti control flow that is difficult to trace, test, and modify. A single change can have unpredictable effects on distant parts of the program.
  2. Monolithic paragraph structure -- Programs with a few enormous paragraphs (500+ lines each) instead of small, focused paragraphs. Makes the program difficult to understand and impossible to unit-test individual functions.
  3. Hardcoded literals ("magic numbers") -- Values like IF WS-CODE = 47 scattered throughout without explanation. Makes maintenance error-prone because the meaning of 47 is unclear and changing the value requires finding every occurrence.
  4. Dead code -- Paragraphs, variables, and conditions that are never executed or referenced. Clutters the program, confuses maintainers, and may trigger false positives in code analysis.
  5. Copybook duplication -- The same data structure defined in multiple programs without using COPY. Changes must be applied to every copy, and inconsistencies inevitably arise.

Exercise 2: Modernization Strategy Definitions

Define each of the following modernization strategies and provide one sentence describing when each is most appropriate:

  1. Rehost
  2. Replatform
  3. Refactor
  4. Rearchitect
  5. Replace
  6. Retain (encapsulate)

Solution:

  1. Rehost (lift and shift): Move the application as-is to a new infrastructure (e.g., mainframe to cloud COBOL runtime) without changing code. Appropriate when the primary goal is reducing infrastructure costs with minimal risk.
  2. Replatform: Move the application to a new platform with minor adaptations (e.g., mainframe COBOL to Micro Focus COBOL on Linux). Appropriate when the target platform offers cost savings and the code needs only minor adjustments.
  3. Refactor: Restructure the existing COBOL code to improve maintainability without changing its external behavior. Appropriate when the business logic is sound but the code structure impedes maintenance.
  4. Rearchitect: Redesign the application's architecture (e.g., monolith to microservices) while preserving the business logic. Appropriate when the current architecture cannot support new business requirements like real-time processing or API access.
  5. Replace: Retire the COBOL application entirely and implement a commercial off-the-shelf (COTS) or custom-built replacement. Appropriate when the business process has fundamentally changed and the existing code cannot be adapted.
  6. Retain (encapsulate): Keep the legacy code running but wrap it with modern interfaces (APIs, message queues). Appropriate when the code is stable, well-tested, and the business logic is too complex or risky to rewrite.

Exercise 3: GO TO Classification

Classify each of the following GO TO usages as either (A) eliminable through simple restructuring, (B) a forward-branch that can become an IF/END-IF, or (C) a backward-branch that should become a PERFORM loop:

      * Usage 1:
           IF WS-ERROR-FLAG = 1
               GO TO 9999-EXIT
           END-IF

      * Usage 2:
       1000-LOOP.
           READ INPUT-FILE INTO WS-RECORD
           IF WS-EOF-FLAG = 1
               GO TO 1000-EXIT
           END-IF
           PERFORM 2000-PROCESS-RECORD
           GO TO 1000-LOOP.
       1000-EXIT.

      * Usage 3:
           GO TO 3100-STEP-A
                3200-STEP-B
                3300-STEP-C
               DEPENDING ON WS-STEP-NUM.

      * Usage 4:
           IF WS-AMOUNT < 0
               GO TO 5000-REJECT
           END-IF
           IF WS-AMOUNT > 99999
               GO TO 5000-REJECT
           END-IF
           PERFORM 5100-ACCEPT.
           GO TO 5200-CONTINUE.
       5000-REJECT.
           MOVE "INVALID" TO WS-STATUS.
       5200-CONTINUE.

Solution:

  1. (B) Forward branch -- This is an early-exit pattern. It can be restructured with a PERFORM-through or by wrapping the remaining logic in an IF condition.
  2. (C) Backward branch -- The GO TO 1000-LOOP creates a loop. This should become PERFORM 1000-LOOP UNTIL WS-EOF-FLAG = 1 with the read and process logic inside.
  3. (A) Eliminable -- GO TO DEPENDING ON is directly replaceable with EVALUATE/WHEN. Each target paragraph becomes a WHEN clause.
  4. (B) Forward branch -- The two GO TOs to 5000-REJECT skip the accept logic. This can be restructured with nested IF/ELSE: IF WS-AMOUNT >= 0 AND WS-AMOUNT <= 99999 THEN ACCEPT ELSE REJECT.

Exercise 4: Strangler Fig Pattern

Explain the Strangler Fig pattern for legacy modernization. Answer these questions: 1. Where does the name come from? 2. What are the three phases of the pattern? 3. Why is it preferred over "big bang" replacement?

Solution:

  1. The name comes from the strangler fig tree, which grows around an existing tree, gradually replacing it. Eventually the original tree dies and the fig stands on its own. Similarly, the new system gradually replaces the old system's functionality.
  2. The three phases are: (a) Transform -- build new functionality that replaces a specific piece of the old system. (b) Coexist -- run both old and new simultaneously, routing traffic to the appropriate system. (c) Eliminate -- once the new component is proven, decommission the corresponding old component.
  3. It is preferred over big-bang replacement because it reduces risk. Each increment is small, testable, and reversible. If the new component fails, traffic can be routed back to the old system. The organization sees incremental value rather than waiting years for a complete replacement. Business operations are never disrupted.

Exercise 5: Data Modernization Concepts

Define each of the following data modernization terms:

  1. VSAM to DB2 migration
  2. EBCDIC to ASCII conversion
  3. Packed decimal to binary conversion
  4. Copybook extraction to JSON schema
  5. Date field expansion (Y2K-style)

Solution:

  1. VSAM to DB2 migration -- Moving data from VSAM file structures (KSDS, ESDS, RRDS) to DB2 relational database tables. This enables SQL access, better data sharing, and integration with modern analytics tools.
  2. EBCDIC to ASCII conversion -- Converting character data from EBCDIC encoding (used on IBM mainframes) to ASCII/UTF-8 (used on distributed systems). Required when moving data or programs off the mainframe.
  3. Packed decimal to binary conversion -- Converting COMP-3 (packed decimal) numeric fields to COMP (binary) or to standard integer/decimal types used by modern databases and languages.
  4. Copybook extraction to JSON schema -- Analyzing COBOL copybook layouts (01-level with PIC clauses) and generating equivalent JSON schema definitions. This enables non-COBOL systems to understand and validate data exchanged with the COBOL program.
  5. Date field expansion -- Expanding 2-digit year fields (PIC 9(2) or PIC 9(6) for YYMMDD) to 4-digit year fields (PIC 9(4) or PIC 9(8) for YYYYMMDD). Originally driven by Y2K but still relevant for legacy systems that were patched with windowing techniques rather than proper expansion.

Exercise 6: Code Smell Identification

Examine the following code fragment and list every "code smell" (maintenance problem) you can find:

       2000-PROC.
           MOVE 0 TO A B C D
           GO TO 2100-X.
       2100-X.
           READ INFILE INTO R AT END GO TO 2900-END.
           IF R-CODE = 'A' ADD R-AMT TO A GO TO 2100-X.
           IF R-CODE = 'B' ADD R-AMT TO B GO TO 2100-X.
           IF R-CODE = 'C' ADD R-AMT TO C GO TO 2100-X.
           ADD R-AMT TO D.
           GO TO 2100-X.
       2900-END.
           DISPLAY A B C D.

Solution:

  1. Single-letter variable names (A, B, C, D, R) -- No indication of purpose.
  2. GO TO loop -- The backward GO TO to 2100-X creates an implicit loop that should be PERFORM UNTIL.
  3. Inline READ with GO TO -- AT END with GO TO is the pre-structured programming pattern. Should use PERFORM with EOF flag.
  4. No END-IF terminators -- The cascading IF statements without END-IF are fragile and hard to modify.
  5. Magic literal codes -- 'A', 'B', 'C' are unexplained. Should use 88-level condition names.
  6. No paragraph separation -- Multiple responsibilities crammed into one flow (read, categorize, accumulate, display).
  7. Inline processing -- The DISPLAY at 2900-END mixes I/O with computation. Accumulation and reporting should be separate.
  8. Unnecessary GO TO -- The GO TO 2100-X after MOVE 0 TO A B C D is pointless since 2100-X is the next sequential paragraph.

Tier 2: Understand (Exercises 7-13)

These exercises require explaining concepts, comparing approaches, and analyzing legacy code.

Exercise 7: GO TO Elimination -- Forward Branch

Rewrite the following GO TO forward-branch pattern using structured IF/ELSE:

       3000-VALIDATE.
           IF WS-NAME = SPACES
               MOVE "NAME REQUIRED" TO WS-ERROR
               GO TO 3000-EXIT
           END-IF
           IF WS-AMOUNT < 0
               MOVE "INVALID AMOUNT" TO WS-ERROR
               GO TO 3000-EXIT
           END-IF
           IF WS-DATE NOT NUMERIC
               MOVE "INVALID DATE" TO WS-ERROR
               GO TO 3000-EXIT
           END-IF
           MOVE "VALID" TO WS-STATUS
           PERFORM 3100-PROCESS-RECORD.
       3000-EXIT.
           EXIT.

Solution:

       3000-VALIDATE.
           MOVE SPACES TO WS-ERROR
           EVALUATE TRUE
               WHEN WS-NAME = SPACES
                   MOVE "NAME REQUIRED" TO WS-ERROR
               WHEN WS-AMOUNT < 0
                   MOVE "INVALID AMOUNT" TO WS-ERROR
               WHEN WS-DATE NOT NUMERIC
                   MOVE "INVALID DATE" TO WS-ERROR
               WHEN OTHER
                   MOVE "VALID" TO WS-STATUS
                   PERFORM 3100-PROCESS-RECORD
           END-EVALUATE
           .

The EVALUATE replaces the cascading IF/GO TO pattern. Each validation check is a WHEN clause, and the happy path (all validations pass) is in WHEN OTHER.


Exercise 8: GO TO Elimination -- Backward Branch (Loop)

Rewrite this GO TO-based loop using PERFORM UNTIL:

       5000-CALC-TOTAL.
           MOVE 0 TO WS-TOTAL.
           MOVE 0 TO WS-COUNT.
       5100-LOOP.
           READ TRANS-FILE INTO WS-TRANS-REC
               AT END GO TO 5200-DONE.
           ADD WS-TRANS-AMOUNT TO WS-TOTAL.
           ADD 1 TO WS-COUNT.
           IF WS-TRANS-AMOUNT > WS-LIMIT
               PERFORM 6000-FLAG-EXCEPTION
           END-IF.
           GO TO 5100-LOOP.
       5200-DONE.
           IF WS-COUNT > 0
               COMPUTE WS-AVERAGE = WS-TOTAL / WS-COUNT
           END-IF.

Solution:

       5000-CALC-TOTAL.
           MOVE 0 TO WS-TOTAL
           MOVE 0 TO WS-COUNT
           MOVE "N" TO WS-EOF-FLAG

           PERFORM 5100-READ-AND-PROCESS
               UNTIL WS-EOF-FLAG = "Y"

           IF WS-COUNT > 0
               COMPUTE WS-AVERAGE = WS-TOTAL / WS-COUNT
           END-IF
           .

       5100-READ-AND-PROCESS.
           READ TRANS-FILE INTO WS-TRANS-REC
               AT END
                   MOVE "Y" TO WS-EOF-FLAG
               NOT AT END
                   ADD WS-TRANS-AMOUNT TO WS-TOTAL
                   ADD 1 TO WS-COUNT
                   IF WS-TRANS-AMOUNT > WS-LIMIT
                       PERFORM 6000-FLAG-EXCEPTION
                   END-IF
           END-READ
           .

Exercise 9: Paragraph Extraction

The following paragraph does too many things. Identify the distinct responsibilities and extract each into its own paragraph:

       2000-PROCESS.
           READ INPUT-FILE INTO WS-INPUT-REC
               AT END MOVE "Y" TO WS-EOF.
           IF WS-EOF = "Y"
               GO TO 2000-EXIT
           END-IF.
           IF WS-INPUT-TYPE = "H"
               MOVE WS-INPUT-DATE TO WS-HDR-DATE
               MOVE WS-INPUT-BATCH TO WS-HDR-BATCH
               ADD 1 TO WS-HDR-COUNT
           END-IF.
           IF WS-INPUT-TYPE = "D"
               MOVE WS-INPUT-ACCT TO WS-DTL-ACCT
               MOVE WS-INPUT-AMT TO WS-DTL-AMT
               ADD WS-DTL-AMT TO WS-BATCH-TOTAL
               ADD 1 TO WS-DTL-COUNT
               IF WS-DTL-AMT > 10000
                   MOVE "Y" TO WS-REVIEW-FLAG
                   ADD 1 TO WS-REVIEW-COUNT
               END-IF
               WRITE OUTPUT-REC FROM WS-DTL-LINE
           END-IF.
           IF WS-INPUT-TYPE = "T"
               IF WS-BATCH-TOTAL NOT = WS-INPUT-AMT
                   DISPLAY "BATCH TOTAL MISMATCH"
                   MOVE "Y" TO WS-ERROR-FLAG
               END-IF
           END-IF.
       2000-EXIT.
           EXIT.

Solution:

       2000-PROCESS.
           PERFORM 2100-READ-INPUT
           IF WS-EOF NOT = "Y"
               EVALUATE WS-INPUT-TYPE
                   WHEN "H"
                       PERFORM 2200-PROCESS-HEADER
                   WHEN "D"
                       PERFORM 2300-PROCESS-DETAIL
                   WHEN "T"
                       PERFORM 2400-PROCESS-TRAILER
               END-EVALUATE
           END-IF
           .

       2100-READ-INPUT.
           READ INPUT-FILE INTO WS-INPUT-REC
               AT END MOVE "Y" TO WS-EOF
           END-READ
           .

       2200-PROCESS-HEADER.
           MOVE WS-INPUT-DATE  TO WS-HDR-DATE
           MOVE WS-INPUT-BATCH TO WS-HDR-BATCH
           ADD 1 TO WS-HDR-COUNT
           .

       2300-PROCESS-DETAIL.
           MOVE WS-INPUT-ACCT TO WS-DTL-ACCT
           MOVE WS-INPUT-AMT  TO WS-DTL-AMT
           ADD WS-DTL-AMT TO WS-BATCH-TOTAL
           ADD 1 TO WS-DTL-COUNT
           IF WS-DTL-AMT > 10000
               PERFORM 2310-FLAG-FOR-REVIEW
           END-IF
           WRITE OUTPUT-REC FROM WS-DTL-LINE
           .

       2310-FLAG-FOR-REVIEW.
           MOVE "Y" TO WS-REVIEW-FLAG
           ADD 1 TO WS-REVIEW-COUNT
           .

       2400-PROCESS-TRAILER.
           IF WS-BATCH-TOTAL NOT = WS-INPUT-AMT
               DISPLAY "BATCH TOTAL MISMATCH"
               MOVE "Y" TO WS-ERROR-FLAG
           END-IF
           .

Exercise 10: Condition Name Refactoring

Replace the following magic numbers and string comparisons with 88-level condition names:

       01  WS-ACCT-STATUS     PIC X(1).
       01  WS-TXN-TYPE        PIC 9(2).
       01  WS-PRIORITY        PIC 9(1).

           IF WS-ACCT-STATUS = "A" OR "R"
               IF WS-TXN-TYPE = 01 OR 02 OR 03
                   IF WS-PRIORITY = 1
                       PERFORM 5000-HIGH-PRIORITY
                   END-IF
               END-IF
           END-IF

Solution:

       01  WS-ACCT-STATUS     PIC X(1).
           88 ACCT-ACTIVE                  VALUE "A".
           88 ACCT-RESTRICTED              VALUE "R".
           88 ACCT-USABLE                  VALUE "A" "R".
       01  WS-TXN-TYPE        PIC 9(2).
           88 TXN-DEPOSIT                  VALUE 01.
           88 TXN-WITHDRAWAL               VALUE 02.
           88 TXN-TRANSFER                 VALUE 03.
           88 TXN-STANDARD                 VALUE 01 THRU 03.
       01  WS-PRIORITY        PIC 9(1).
           88 PRIORITY-HIGH                VALUE 1.
           88 PRIORITY-MEDIUM              VALUE 2.
           88 PRIORITY-LOW                 VALUE 3.

           IF ACCT-USABLE AND TXN-STANDARD
               AND PRIORITY-HIGH
               PERFORM 5000-HIGH-PRIORITY
           END-IF

Exercise 11: Assessing Modernization Risk

A 15,000-line COBOL batch program has the following characteristics: - 147 GO TO statements - 23 paragraphs (average 650 lines each) - 12 COPY members referenced - 3 VSAM files and 1 DB2 table accessed - Last modified 8 months ago (minor bug fix) - 2 developers familiar with the code (one retiring in 6 months) - Processes $4.2 billion in transactions monthly

Rate the modernization urgency as LOW, MEDIUM, or HIGH for each factor and explain your reasoning. What modernization strategy would you recommend?

Solution:

  • GO TO density (147 in 15K lines): HIGH -- Approximately 1 GO TO per 100 lines indicates severely tangled control flow.
  • Paragraph size (avg 650 lines): HIGH -- Extremely large paragraphs are nearly impossible to understand or modify safely.
  • External dependencies (12 copies, 4 files/tables): MEDIUM -- Moderate coupling to external structures; changes in copybooks or file layouts affect this program.
  • Recent modification activity: LOW -- Recent successful modification indicates the code is still maintainable by current staff.
  • Knowledge concentration (2 developers, 1 retiring): HIGH -- Critical knowledge loss imminent. This is the most urgent factor.
  • Business criticality ($4.2B monthly): HIGH -- The consequences of a failure are severe, which paradoxically makes both inaction and aggressive change risky.

Recommended strategy: Refactor incrementally with knowledge capture. Before the experienced developer retires, invest 3-4 months in: (1) documenting the business rules embedded in the code, (2) extracting the large paragraphs into smaller, named paragraphs, (3) eliminating the most dangerous GO TO patterns, and (4) adding inline comments. Do NOT attempt a rewrite or replacement given the business criticality and time pressure.


Exercise 12: Legacy Code Reading

Read the following legacy code and describe in plain English what it does. Identify the business rule it implements:

       4000-CALC.
           MOVE 0 TO T.
           IF C > 50000
               COMPUTE T = (C - 50000) * .03
               MOVE 50000 TO C
           END-IF.
           IF C > 20000
               COMPUTE T = T + (C - 20000) * .02
               MOVE 20000 TO C
           END-IF.
           IF C > 5000
               COMPUTE T = T + (C - 5000) * .01
           END-IF.

Solution:

This implements a tiered/progressive fee or tax calculation on a value C: - On the portion of C above $50,000: charge 3% - On the portion of C between $20,001 and $50,000: charge 2% - On the portion of C between $5,001 and $20,000: charge 1% - On the portion of C at or below $5,000: no charge

The total fee T is accumulated across all tiers. The code destructively modifies C at each tier boundary (capping it for the next tier's calculation). For example, if C = $75,000: T = (75000-50000) * 0.03 + (50000-20000) * 0.02 + (20000-5000) * 0.01 = 750 + 600 + 150 = $1,500.

This is a progressive rate structure commonly used for taxes, transaction fees, or commission calculations.


Exercise 13: Wrapper Pattern Explanation

Explain the wrapper (facade) pattern for legacy COBOL modernization. Describe: 1. What the wrapper does. 2. How the legacy code is invoked. 3. What the external interface looks like. 4. When this approach is preferable to rewriting.

Solution:

  1. What the wrapper does: The wrapper is a new COBOL program (or a z/OS Connect service) that provides a clean, modern interface to external consumers while internally delegating all business logic to the unchanged legacy program. It translates between the modern interface (JSON/REST) and the legacy interface (COMMAREA/file I/O).

  2. How the legacy code is invoked: The wrapper calls the legacy program using CALL or EXEC CICS LINK, passing data through the LINKAGE SECTION or COMMAREA exactly as the legacy program expects. The legacy program runs unchanged.

  3. What the external interface looks like: External consumers see a clean API with well-named fields, proper error codes, and modern data formats (JSON). They are completely unaware that a legacy program exists behind the wrapper.

  4. When this is preferable to rewriting: When the legacy code contains complex, well-tested business logic that has been validated over decades of production use. Rewriting risks introducing bugs in logic that was correct. The wrapper preserves the proven logic while enabling modern access patterns.


Tier 3: Apply (Exercises 14-21)

These exercises require performing actual code refactoring and modernization tasks.

Exercise 14: Complete GO TO Elimination

Refactor the following legacy program to eliminate all GO TO statements. Preserve the exact business logic:

       PROCEDURE DIVISION.
       0000-START.
           OPEN INPUT CUST-FILE OUTPUT REPORT-FILE.
           MOVE 0 TO LINE-CT PAGE-CT TOTAL-AMT.
       0100-READ.
           READ CUST-FILE AT END GO TO 0900-DONE.
           ADD 1 TO LINE-CT.
           IF LINE-CT > 50
               PERFORM 0800-NEW-PAGE
               MOVE 0 TO LINE-CT
           END-IF.
           IF CUST-BAL < 0
               GO TO 0100-READ.
           IF CUST-BAL > 100000
               MOVE "*" TO FLAG-FIELD
           ELSE
               MOVE " " TO FLAG-FIELD.
           ADD CUST-BAL TO TOTAL-AMT.
           WRITE RPT-REC FROM DETAIL-LINE.
           GO TO 0100-READ.
       0800-NEW-PAGE.
           ADD 1 TO PAGE-CT.
           WRITE RPT-REC FROM HDR-LINE AFTER PAGE.
       0900-DONE.
           WRITE RPT-REC FROM TOTAL-LINE.
           CLOSE CUST-FILE REPORT-FILE.
           STOP RUN.

Solution:

       PROCEDURE DIVISION.
       0000-MAIN.
           PERFORM 0100-INITIALIZE
           PERFORM 0200-PROCESS-FILE
               UNTIL WS-EOF-FLAG = "Y"
           PERFORM 0900-FINALIZE
           STOP RUN
           .

       0100-INITIALIZE.
           OPEN INPUT CUST-FILE OUTPUT REPORT-FILE
           MOVE 0 TO LINE-CT PAGE-CT TOTAL-AMT
           MOVE "N" TO WS-EOF-FLAG
           .

       0200-PROCESS-FILE.
           READ CUST-FILE
               AT END
                   MOVE "Y" TO WS-EOF-FLAG
               NOT AT END
                   PERFORM 0300-PROCESS-RECORD
           END-READ
           .

       0300-PROCESS-RECORD.
           ADD 1 TO LINE-CT
           IF LINE-CT > 50
               PERFORM 0800-NEW-PAGE
               MOVE 0 TO LINE-CT
           END-IF

           IF CUST-BAL >= 0
               IF CUST-BAL > 100000
                   MOVE "*" TO FLAG-FIELD
               ELSE
                   MOVE " " TO FLAG-FIELD
               END-IF
               ADD CUST-BAL TO TOTAL-AMT
               WRITE RPT-REC FROM DETAIL-LINE
           END-IF
           .

       0800-NEW-PAGE.
           ADD 1 TO PAGE-CT
           WRITE RPT-REC FROM HDR-LINE AFTER PAGE
           .

       0900-FINALIZE.
           WRITE RPT-REC FROM TOTAL-LINE
           CLOSE CUST-FILE REPORT-FILE
           .

Exercise 15: Magic Number Elimination

Refactor the following code to replace all magic numbers with named constants using 88-level conditions and WORKING-STORAGE constants:

           IF WS-DEPT = 140 OR 220 OR 350
               COMPUTE WS-BONUS = WS-SALARY * 0.15
           ELSE IF WS-DEPT = 410 OR 520
               COMPUTE WS-BONUS = WS-SALARY * 0.10
           ELSE
               COMPUTE WS-BONUS = WS-SALARY * 0.05
           END-IF
           IF WS-YEARS > 10
               ADD 2500 TO WS-BONUS
           ELSE IF WS-YEARS > 5
               ADD 1000 TO WS-BONUS
           END-IF
           IF WS-BONUS > 25000
               MOVE 25000 TO WS-BONUS
           END-IF

Hint: Define 88-levels for department groups, named constants for rates and thresholds.


Exercise 16: Copybook Creation from Inline Definitions

The following data definitions appear in three different programs. Extract them into a single copybook and show how each program would reference it:

      * Program PGMA:
       01  CUSTOMER-RECORD.
           05  CUST-ID         PIC 9(8).
           05  CUST-NAME       PIC X(30).
           05  CUST-ADDR       PIC X(40).
           05  CUST-BAL        PIC S9(9)V99.
           05  CUST-STATUS     PIC X(1).

      * Program PGMB (slightly different):
       01  CUST-REC.
           05  CR-ID           PIC 9(8).
           05  CR-NAME         PIC X(30).
           05  CR-ADDRESS      PIC X(40).
           05  CR-BALANCE      PIC S9(9)V99.
           05  CR-STAT         PIC X(1).

      * Program PGMC (wrong size!):
       01  CUSTOMER.
           05  C-ID            PIC 9(8).
           05  C-NAME          PIC X(25).
           05  C-ADDR          PIC X(40).
           05  C-BAL           PIC S9(7)V99.
           05  C-STATUS        PIC X(1).

Hint: Choose canonical names, ensure all programs use the same sizes, and note the data inconsistency in PGMC that must be investigated.


Exercise 17: Dead Code Removal

Identify and remove all dead code from the following program fragment. Explain why each piece is dead:

       WORKING-STORAGE SECTION.
       01  WS-OLD-RATE         PIC 9V99 VALUE 0.05.
       01  WS-NEW-RATE         PIC 9V99 VALUE 0.07.
       01  WS-TEMP-CALC        PIC 9(9)V99.
       01  WS-UNUSED-FLAG      PIC X VALUE "N".
       01  WS-COUNTER          PIC 9(5) VALUE 0.

       PROCEDURE DIVISION.
           MOVE 0 TO WS-COUNTER
           PERFORM 1000-PROCESS
           PERFORM 2000-REPORT
           STOP RUN.

       1000-PROCESS.
           COMPUTE WS-TEMP-CALC =
               WS-BALANCE * WS-NEW-RATE
           IF 1 = 0
               PERFORM 3000-OLD-CALCULATION
           END-IF
           .

       2000-REPORT.
           DISPLAY "Rate: " WS-NEW-RATE
           DISPLAY "Result: " WS-TEMP-CALC
           .

       3000-OLD-CALCULATION.
           COMPUTE WS-TEMP-CALC =
               WS-BALANCE * WS-OLD-RATE
           ADD 1 TO WS-COUNTER
           .

Hint: Check for unreachable code, unused variables, and conditions that can never be true.


Exercise 18: API Facade for Legacy Program

Write a COBOL wrapper program that provides a clean CALL interface around a legacy program with a poorly designed LINKAGE SECTION. The legacy program expects:

      * Legacy program LGCYPGM expects:
       01  LS-INPUT.
           05  LS-FUNC        PIC 9.     *> 1=Add 2=Del 3=Inq
           05  LS-KEY          PIC X(8).
           05  LS-D1           PIC X(30). *> Name for func 1
           05  LS-D2           PIC 9(7)V99. *> Amount for func 1
           05  LS-RC           PIC 9.     *> 0=OK 1=Err 2=NotFnd

Design a wrapper with clearly named parameters and proper error handling.

Hint: Create separate entry points or an EVALUATE-based dispatch with well-named fields.


Exercise 19: PERFORM THRU Elimination

Refactor the following PERFORM THRU pattern into individual paragraph PERFORMs:

           PERFORM 2000-VALIDATE THRU 2000-VALIDATE-EXIT.
           PERFORM 3000-CALCULATE THRU 3999-CALCULATE-EXIT.

       2000-VALIDATE.
           IF WS-FIELD-A = SPACES
               MOVE "ERR" TO WS-STATUS
               GO TO 2000-VALIDATE-EXIT
           END-IF
           IF WS-FIELD-B NOT NUMERIC
               MOVE "ERR" TO WS-STATUS
               GO TO 2000-VALIDATE-EXIT
           END-IF
           MOVE "OK" TO WS-STATUS.
       2000-VALIDATE-EXIT.
           EXIT.

       3000-CALCULATE.
           COMPUTE WS-RESULT = WS-A + WS-B.
           IF WS-RESULT < 0
               GO TO 3999-CALCULATE-EXIT.
           COMPUTE WS-TAX = WS-RESULT * WS-RATE.
       3999-CALCULATE-EXIT.
           EXIT.

Hint: Replace PERFORM THRU with PERFORM of a single paragraph. Eliminate the GO TO to EXIT paragraphs by using IF/ELSE logic or EVALUATE.


Exercise 20: Data Format Migration

Write a COBOL utility program that reads records in the old format and writes them in the new format:

Old format:

       01  OLD-RECORD.
           05  OLD-DATE        PIC 9(6).    *> YYMMDD
           05  OLD-NAME        PIC X(20).
           05  OLD-AMOUNT      PIC S9(5)V99 COMP-3.
           05  OLD-STATUS      PIC 9.       *> 1=Active 2=Inactive

New format:

       01  NEW-RECORD.
           05  NEW-DATE        PIC 9(8).    *> YYYYMMDD
           05  NEW-NAME        PIC X(30).
           05  NEW-AMOUNT      PIC S9(9)V99.
           05  NEW-STATUS      PIC X(8).    *> "ACTIVE"/"INACTIVE"

Hint: Handle the Y2K date conversion (YY 00-49 = 20xx, YY 50-99 = 19xx), pad the name field, convert packed decimal, and translate status codes.


Exercise 21: Incremental Refactoring Plan

Given a 5,000-line COBOL program with 80 GO TO statements and 15 paragraphs, create a step-by-step refactoring plan. Each step should be a single, testable change. Order the steps from least risky to most risky.

Hint: Start with changes that have no behavioral impact (adding condition names, adding END-IF terminators), then progress to GO TO elimination starting with the simplest patterns (forward branches to exit paragraphs).


Tier 4: Analyze (Exercises 22-27)

These exercises require evaluating strategies, making trade-off decisions, and analyzing complex scenarios.

Exercise 22: Modernization Strategy Selection

A 40-year-old insurance claims processing system has these characteristics: - 2.1 million lines of COBOL across 340 programs - Processes $8 billion in claims annually - 3 developers remaining (average age 58) - 47% of programs have no documentation - Regulatory requirements change 3-4 times per year - Average time to implement a regulatory change: 14 weeks

Evaluate each modernization strategy (rehost, replatform, refactor, rearchitect, replace, retain) and recommend an approach. Justify your choice with specific arguments.

Hint: Consider the constraints: business risk, available expertise, regulatory agility, timeline, and budget.


Exercise 23: Strangler Fig Implementation Plan

Design a Strangler Fig implementation plan for converting a COBOL-based order processing system to microservices. The system has four major subsystems: 1. Order entry (online, CICS-based) 2. Inventory check (batch + online) 3. Pricing/discount calculation (online) 4. Shipping/fulfillment (batch)

For each subsystem, determine: the order of migration, the coexistence strategy during transition, the routing mechanism between old and new, and the risk mitigation approach.

Hint: Start with the subsystem that has the most change requests and the cleanest interface boundaries.


Exercise 24: Refactoring Risk Assessment

A developer proposes refactoring a 3,000-line paragraph into 20 smaller paragraphs. The program processes payroll for 45,000 employees and runs weekly. Assess the risks of this refactoring and propose a testing strategy to mitigate them.

Hint: Consider data flow through the paragraph, shared variables, the impact of PERFORM vs. inline execution on the program's state, and regression testing requirements.


Exercise 25: Legacy vs. Modern Architecture Comparison

Compare a traditional COBOL batch architecture with a modern event-driven architecture for the same business process: end-of-day account reconciliation.

Analyze: processing model, error handling, recovery approach, scalability, and operational monitoring.

Hint: The batch approach reads all transactions sequentially and produces a reconciliation report. The event-driven approach processes each transaction as it occurs and maintains running reconciliation state.


Exercise 26: Knowledge Preservation Strategy

An organization has 5 COBOL developers maintaining 500 programs. Two developers are retiring in 12 months. Design a knowledge preservation strategy that captures the critical business logic and system knowledge before they leave.

Hint: Consider code archaeology (reading code to extract rules), developer interviews, automated documentation tools, and hands-on mentoring.


Exercise 27: Cost-Benefit Analysis of Modernization

Calculate the 5-year total cost of ownership for three scenarios for a COBOL claims system: 1. Maintain as-is on mainframe ($X/year maintenance + $Y/year infrastructure). 2. Refactor COBOL and wrap with APIs ($Z upfront + reduced maintenance). 3. Replace with COTS package ($W upfront + licensing + customization).

Given: Current annual maintenance = $2.5M, infrastructure = $1.8M, regulatory changes = 4/year at $150K each. Use reasonable assumptions for the other scenarios.

Hint: Include hidden costs: staff turnover, recruitment difficulty for COBOL skills, training costs, and risk of implementation failure.


Tier 5: Create (Exercises 28-32)

These exercises require performing substantial modernization work.

Exercise 28: Complete Program Refactoring

Take the following 200-line legacy program and refactor it completely: eliminate all GO TOs, extract paragraphs, add 88-levels, replace magic numbers, and add proper error handling. The refactored program must produce identical output.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. LEGYPRG1.
       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT INFILE ASSIGN TO "INPUT.DAT"
               ORGANIZATION IS SEQUENTIAL.
           SELECT OUTFILE ASSIGN TO "OUTPUT.DAT"
               ORGANIZATION IS SEQUENTIAL.
       DATA DIVISION.
       FILE SECTION.
       FD INFILE.
       01 INREC              PIC X(80).
       FD OUTFILE.
       01 OUTREC             PIC X(132).
       WORKING-STORAGE SECTION.
       01 WR.
         05 T PIC X(1).
         05 C PIC 9(3).
         05 N PIC X(20).
         05 A PIC 9(7)V99.
         05 S PIC X(1).
       01 E PIC X VALUE "N".
       01 TC PIC 9(5) VALUE 0.
       01 TA PIC 9(9)V99 VALUE 0.
       01 EC PIC 9(5) VALUE 0.
       01 LC PIC 9(3) VALUE 0.
       01 PC PIC 9(3) VALUE 0.
       01 OL PIC X(132) VALUE SPACES.
       PROCEDURE DIVISION.
       S. OPEN INPUT INFILE OUTPUT OUTFILE.
       R. READ INFILE INTO WR AT END MOVE "Y" TO E.
          IF E = "Y" GO TO D.
          IF T = "H" GO TO H.
          IF T = "D" GO TO P.
          IF T = "T" GO TO V.
          GO TO R.
       H. ADD 1 TO PC.
          MOVE SPACES TO OL.
          STRING "PAGE " PC DELIMITED SIZE INTO OL.
          WRITE OUTREC FROM OL AFTER PAGE.
          MOVE 0 TO LC.
          GO TO R.
       P. IF S NOT = "A" AND NOT = "P" GO TO R.
          ADD 1 TO TC. ADD A TO TA. ADD 1 TO LC.
          IF LC > 55 GO TO H.
          MOVE SPACES TO OL.
          STRING C " " N " " A " " S
              DELIMITED SIZE INTO OL.
          WRITE OUTREC FROM OL.
          GO TO R.
       V. IF TA NOT = A
              ADD 1 TO EC
              MOVE SPACES TO OL
              STRING "*** TOTAL MISMATCH: " TA " vs " A
                  DELIMITED SIZE INTO OL
              WRITE OUTREC FROM OL.
          GO TO R.
       D. MOVE SPACES TO OL.
          STRING "RECORDS: " TC " TOTAL: " TA " ERRORS: " EC
              DELIMITED SIZE INTO OL.
          WRITE OUTREC FROM OL.
          CLOSE INFILE OUTFILE. STOP RUN.

Hint: First understand what it does, then rename variables, then restructure the control flow.


Exercise 29: API Wrapper with Error Translation

Design and implement a comprehensive API wrapper that exposes three legacy COBOL programs (customer inquiry, account update, transaction posting) through a single modern interface. The wrapper should: - Accept a well-structured request through the LINKAGE SECTION. - Dispatch to the appropriate legacy program. - Translate legacy error codes to modern error structures. - Log all interactions for audit. - Handle legacy program failures gracefully.

Hint: Use an EVALUATE-based dispatcher. Define a comprehensive error code mapping table.


Exercise 30: Data Migration Utility

Write a complete data migration utility that converts a VSAM KSDS file from legacy format to modern format. The utility should: - Read the legacy VSAM file. - Apply data transformations (date expansion, field widening, code translation). - Validate each record against business rules. - Write valid records to the new file. - Write rejected records to an error file with reason codes. - Produce a migration summary report.

Hint: Define transformation rules in a table for maintainability.


Exercise 31: Automated Documentation Generator

Write a COBOL program that reads a COBOL source file as input and produces a documentation report listing: - All paragraph names with their line numbers. - All COPY statements and the copybooks referenced. - All CALL statements and the programs called. - All file names and their access modes. - All GO TO statements and their targets. - A summary of code metrics (total lines, comment lines, blank lines, paragraphs, GO TOs).

Hint: Read the source file line by line, parse each line for keywords, and accumulate statistics.


Exercise 32: Modernization Proof of Concept

Implement a proof-of-concept for modernizing a batch COBOL report program into a near-real-time service: 1. The original batch program reads a sequential file and produces a formatted report. 2. The modernized version wraps the core business logic in a callable module. 3. A new driver program invokes the module for individual records passed through the LINKAGE SECTION. 4. The module returns formatted output suitable for JSON conversion.

Write all three components: the original batch program, the extracted business logic module, and the new service driver.

Hint: The key challenge is separating the business logic (which should be preserved) from the I/O and control flow (which changes between batch and service modes).


General Guidelines

  • When refactoring, always verify that the external behavior remains identical.
  • Test refactored code with the same inputs as the original and compare outputs.
  • Document the purpose of each change so future maintainers understand the rationale.
  • Prefer small, incremental changes over large restructurings.
  • Always maintain a working version of the program that can be deployed at any point during the refactoring process.