COBOL COBOL 2014 (ISO/IEC 1989:2014), built with GnuCOBOL 3.x ·
✓ verified by execution on 2026-08-16
When you encounter legacy code that has been running untouched for decades, the prospect of changing it can be terrifying. Many legacy programs rely on complex, unstructured GO TO loops and overlapping memory spaces (REDEFINES). Modifying such code without a safety net often introduces subtle bugs that are difficult to trace.
Refactoring is the process of changing the internal structure of code without altering its external behavior. In COBOL, this means transforming “spaghetti code” into structured, readable logic. However, you should never attempt this without regression testing.
The Regression Test Harness
A test harness isolates the behavior of a program segment so it can be verified independently. This baseline is your safety net. You run the original code, capture its outputs and side-effects, and assert that the new code produces identically the same results.
Below is an example of an unmodified legacy program wrapped in a simple test harness:
Refactoring should be performed in small, verifiable steps. After every minor change, you rerun your test harness. If the output diverges, you immediately know which step broke the logic.
Here is the same test harness, but the LEGACY-CODE has been refactored into NEW-CODE:
Notice that the output remains precisely the same. Behavior preservation means the outputs of the old and new logic are identical for all inputs. Refactoring changes how the program is written without changing what it does.
Untangling GO TO Logic
One of the most common refactoring tasks in COBOL is removing GO TO statements in favor of structured PERFORM or IF/ELSE blocks.
Consider this unstructured snippet:
cobol✓ verified output
IDENTIFICATION DIVISION. PROGRAM-ID. Before. PROCEDURE DIVISION. P1. GO TO P2. P2. DISPLAY "X". STOP RUN.
The exact behavior is maintained, but the flow of control is now predictable and structured.
Visualizing the Flow
Predict the Output
Before proceeding, let’s verify you understand the behavior of the refactored complex logic.
Predict the outputcobol
Read the code. What exactly will it print? Commit to an answer before you look.
IDENTIFICATION DIVISION. PROGRAM-ID. ComplexR. DATA DIVISION. WORKING-STORAGE SECTION. 01 VAL PIC 9 VALUE 1. PROCEDURE DIVISION. P1. ADD 1 TO VAL. GO TO P3. P2. ADD 2 TO VAL. P3. ADD 3 TO VAL. DISPLAY VAL. STOP RUN.
Output
5
You predicted
7
5
4
The program skips P2 because of the GO TO P3 statement. So VAL becomes 1 + 1 + 3 = 5.
Check yourself
What is the primary purpose of a test harness when refactoring?
Reveal answer
To establish a behavioral baseline and prevent regressions. — A test harness is designed to lock in the current behavior of a system, catching any unintended side effects when the internal code is altered.
What does 'behavior preservation' mean in the context of refactoring?
Reveal answer
The outputs of the old and new logic are identical for all inputs. — Refactoring changes how the code is written, but the outputs must remain identical for the same inputs.
Why is refactoring COBOL GO TO statements critical?
Reveal answer
Because GO TO logic is often unstructured, making the control flow hard to follow and modify safely. — GO TO statements often create spaghetti code that is difficult to understand and inherently unsafe to modify.
Challenges
Challenge 1 +50 XP
We need a safety net for LEGACY-TAX. Write a RUN-TEST paragraph that displays 'BEFORE: ' followed by TAX-VAL, PERFORMs LEGACY-TAX, and then displays 'AFTER: ' followed by TAX-VAL. Call RUN-TEST from the main procedure and STOP RUN.
cobol✓ solution verified at build time
COBOL has no in-browser runtime, so this one is pen-and-paper: work out what the
fixed code should be, then open the solution and compare. The expected output
below is real — it came from compiling the solution with GnuCOBOL when this page
was built.
A developer rewrote the LEGACY-DISCOUNT paragraph from scratch, but it fails for edge cases. Find and fix the bug in NEW-DISCOUNT so it exactly matches the output of LEGACY-DISCOUNT for all inputs.
This code runs. It just does the wrong thing. Read it, find the defect,
fix it — the tests below decide when you are right.
cobol✓ solution verified at build time
COBOL has no in-browser runtime, so this one is pen-and-paper: work out what the
fixed code should be, then open the solution and compare. The expected output
below is real — it came from compiling the solution with GnuCOBOL when this page
was built.
Test 1 (input: "60") — expects "DISCOUNT: 20\n"
Test 2 (input: "40") — expects "DISCOUNT: 10\n"
Test 3 (input: "20") — expects "DISCOUNT: 00\n"
Show solution (0 XP)
IDENTIFICATION DIVISION. PROGRAM-ID. BugHuntRefactor. DATA DIVISION. WORKING-STORAGE SECTION. 01 PRICE PIC 99. 01 DISCOUNT PIC 99 VALUE 0. PROCEDURE DIVISION. ACCEPT PRICE. PERFORM NEW-DISCOUNT. DISPLAY 'DISCOUNT: ' DISCOUNT. STOP RUN. LEGACY-DISCOUNT. IF PRICE > 50 GO TO HIGH-DISCOUNT. IF PRICE > 30 GO TO MED-DISCOUNT. GO TO END-DISCOUNT. HIGH-DISCOUNT. ADD 20 TO DISCOUNT. GO TO END-DISCOUNT. MED-DISCOUNT. ADD 10 TO DISCOUNT. END-DISCOUNT. EXIT. NEW-DISCOUNT. IF PRICE > 50 ADD 20 TO DISCOUNT ELSE IF PRICE > 30 ADD 10 TO DISCOUNT END-IF END-IF.