Reading a Program You Did Not Write

COBOL GnuCOBOL 3.1.2 · ✓ verified by execution on 2026-08-16

When maintaining COBOL applications, you will spend far more time reading code than writing it. Many systems you encounter will have been written decades ago, relying on unstructured programming techniques that modern languages typically avoid.

This lesson is your guide to code archaeology. We will explore how control flows in legacy applications and how to trace execution through unstructured logic like GO TO, ALTER, and paragraph fall-throughs.

Tracing a One-Way Jump

The first thing that defeats a modern reader is that control does not come back. PERFORM returns to its caller, so you can read a paragraph, note “this calls that”, and carry on. GO TO is a one-way jump: control leaves and never returns, so the paragraph you are reading has no ending you can rely on.

That changes how you read. You cannot scan a program top to bottom and assume the order on the page is the order of execution. You have to trace — pick an entry point, follow each jump, and keep your own note of where control actually went.

(The next lesson covers GO TO, GO TO DEPENDING ON and their structured replacements properly. Here we only need enough to follow the flow.)

GO TO TARGET PERFORM TARGET
cobol ✓ verified output
       IDENTIFICATION DIVISION.
       PROGRAM-ID. GotoUncond.
       PROCEDURE DIVISION.
           DISPLAY "START".
           GO TO END-PARA.
           DISPLAY "SKIPPED".
       END-PARA.
           DISPLAY "END".
           STOP RUN.
Output
START
END

Because GO TO does not return, following the logic of a program heavily reliant on it requires tracing every jump manually.

When the Code You Read Is Not the Code That Runs

Everything above assumes the jump goes where the source says it goes. ALTER breaks that assumption: it rewrites the target of a GO TO at run time, from somewhere else entirely.

This is the single most important thing to know when reading old COBOL, because it defeats reading. You can study a paragraph, understand it completely, and still be wrong about where it goes — the answer is in a different paragraph that ran earlier. When tracing unfamiliar code, search the whole program for ALTER before you trust any GO TO you have read.

Predict the output cobol

Read the code. What exactly will it print? Commit to an answer before you look.

       ID DIVISION. PROGRAM-ID. A.
       PROCEDURE DIVISION.
       P-1.
           DISPLAY "1".
           ALTER P-3 TO PROCEED TO P-4.
           GO TO P-3.
       P-2.
           DISPLAY "2". STOP RUN.
       P-3.
           GO TO P-2.
       P-4.
           DISPLAY "4". STOP RUN.
Output
1
4

In the example above, if you only looked at PARA-C, you would assume it jumps to PARA-B. However, PARA-A silently rewires it to jump to PARA-E instead!

Fall-through and Dead Code

Another common source of confusion in legacy COBOL is paragraph execution. Paragraphs do not inherently isolate code like functions do in modern languages. Unless you explicitly transfer control (using GO TO, PERFORM, or STOP RUN), execution “falls through” from the end of one paragraph straight into the beginning of the next.

cobol ✓ verified output
       IDENTIFICATION DIVISION.
       PROGRAM-ID. Fallthrough.
       PROCEDURE DIVISION.
       START-PARA.
           DISPLAY "START".
       NEXT-PARA.
           DISPLAY "NEXT".
           STOP RUN.
Output
START
NEXT

Because execution can jump unpredictably and fall through without warning, many legacy programs contain dead code—paragraphs that are completely unreachable. Unreachable paragraphs remain in the program, clutter the logic, and must be manually refactored.

Refactoring with PERFORM

Modern COBOL replaces GO TO and ALTER with the PERFORM statement. The PERFORM statement transfers control explicitly to one or more procedures and implicitly returns control to the next executable statement. This keeps your code structured and predictable.

If you must execute a sequence of paragraphs, you can use PERFORM THRU.

cobol ✓ verified output
       IDENTIFICATION DIVISION.
       PROGRAM-ID. PerformThru.
       PROCEDURE DIVISION.
           PERFORM PARA-1 THRU PARA-3.
           DISPLAY "MAIN-END".
           STOP RUN.
       PARA-1.
           DISPLAY "1".
       PARA-2.
           DISPLAY "2".
       PARA-3.
           DISPLAY "3".
       PARA-4.
           DISPLAY "4".
Output
1
2
3
MAIN-END

The exercises for this lesson challenge you to find dead code and refactor spaghetti code using PERFORM. Good luck with your archaeology!

Check yourself

Why is the ALTER statement considered dangerous in legacy code?

Reveal answer

It changes the destination of a GO TO statement dynamically at runtime, making static analysis impossible. — ALTER changes the behavior of GO TO statements at runtime, meaning you cannot trust what the source code says just by reading it.

In COBOL, what happens when execution reaches the end of a paragraph if there is no explicit jump or stop?

Reveal answer

Execution falls through to the next paragraph in the source file. — Unless stopped by STOP RUN, EXIT PROGRAM, or redirected by a GO TO, execution naturally falls through to the next paragraph.

Why is dead code (unreachable paragraphs) a problem in legacy COBOL?

Reveal answer

It clutters the program, making the logic difficult to follow and maintain. — Unreachable paragraphs remain in the source file and confuse maintainers who must read and trace the logic manually.

Challenges

Challenge 1 +50 XP

Identify and remove the dead code (unreachable paragraphs) in this program. The output should just be 'START' followed by 'END'.

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 — expects "START\nEND\n"
Need a hint? (−25% XP)

Remove any paragraphs that can never be reached by execution.

Show solution (0 XP)
IDENTIFICATION DIVISION.
PROGRAM-ID. DeadCode.
PROCEDURE DIVISION.
    DISPLAY "START".
    GO TO END-PARA.
END-PARA.
    DISPLAY "END".
    STOP RUN.

Challenge 2 +50 XP

Refactor this unstructured program to use a single PERFORM statement instead of GO TOs.

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 — expects "WORKING\nDONE\n"
Need a hint? (−25% XP)

Use PERFORM DO-WORK in the main procedure and remove the GO TOs.

Show solution (0 XP)
IDENTIFICATION DIVISION.
PROGRAM-ID. FixSpaghetti.
PROCEDURE DIVISION.
    PERFORM DO-WORK.
    DISPLAY "DONE".
    STOP RUN.
DO-WORK.
    DISPLAY "WORKING".

🐞 Bug Hunt +50 XP

This program is supposed to check a status code and display only one message. But the developer assumed paragraphs act like isolated functions. Run the code to see what happens, then fix the bug so it only prints 'Valid'.

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 — expects "Valid\n"
Need a hint? (−25% XP)

Unless you explicitly stop the program or jump, COBOL will fall through from VALID-STATUS straight into INVALID-STATUS.

Show solution (0 XP)
IDENTIFICATION DIVISION.
PROGRAM-ID. CheckStatus.
DATA DIVISION.
WORKING-STORAGE SECTION.
01  STATUS-CODE PIC 9 VALUE 0.
PROCEDURE DIVISION.
    IF STATUS-CODE = 0
        GO TO VALID-STATUS
    ELSE
        GO TO INVALID-STATUS
    END-IF.

VALID-STATUS.
    DISPLAY "Valid".
    STOP RUN.
    
INVALID-STATUS.
    DISPLAY "Invalid".
    STOP RUN.