COBOL Control Flow: IF, EVALUATE, and PERFORM Explained

Control flow determines how COBOL programs make decisions and repeat work. Three statements handle the vast majority of COBOL logic: IF for conditional branching, EVALUATE for multi-way dispatch, and PERFORM for iteration and modular calls. Understanding all three — and their variants — is essential for writing and reading production mainframe code.
IF Statement
Basic IF/ELSE/END-IF
IF WS-ACCOUNT-STATUS = 'AC'
PERFORM PROCESS-ACTIVE-ACCOUNT
ELSE
PERFORM LOG-INACTIVE-ACCOUNT
END-IF.The END-IF scope terminator explicitly closes the IF block. Always use END-IF in modern COBOL. The older style using a period to close the IF (IF condition ... action.) terminates all open blocks including enclosing IFs and PERFORMs — a common source of subtle bugs.
Compound Conditions
IF WS-AMOUNT > ZERO
AND WS-ACCOUNT-STATUS = 'AC'
AND NOT WS-SUSPENDED
PERFORM PROCESS-PAYMENT
ELSE
PERFORM REJECT-PAYMENT
END-IF.AND, OR, and NOT are the logical operators. Standard precedence: NOT first, then AND, then OR. Use parentheses to override:
IF (WS-STATUS = 'AC' OR WS-STATUS = 'PD')
AND WS-AMOUNT > ZERO
PERFORM ALLOW-TRANSACTION
END-IF.Abbreviated Conditions
COBOL allows abbreviated compound conditions that re-use the subject:
*> Full form:
IF WS-CODE = 'A' OR WS-CODE = 'B' OR WS-CODE = 'C'
*> Abbreviated:
IF WS-CODE = 'A' OR 'B' OR 'C'This only works when the relational operator and subject are the same. It does not work when operators differ.
Nested IF
IF WS-TRANSACTION-TYPE = 'CREDIT'
IF WS-AMOUNT > WS-CREDIT-LIMIT
MOVE 'LIMIT EXCEEDED' TO WS-MESSAGE
SET WS-ERROR-FOUND TO TRUE
ELSE
PERFORM APPLY-CREDIT
END-IF
ELSE IF WS-TRANSACTION-TYPE = 'DEBIT'
IF WS-AMOUNT > WS-AVAILABLE-BALANCE
MOVE 'INSUFFICIENT FUNDS' TO WS-MESSAGE
SET WS-ERROR-FOUND TO TRUE
ELSE
PERFORM APPLY-DEBIT
END-IF
ELSE
MOVE 'UNKNOWN TYPE' TO WS-MESSAGE
SET WS-ERROR-FOUND TO TRUE
END-IF.Limit nesting to two or three levels. Deeper nesting belongs in separate paragraphs.
88-Level Conditions in IF
88-level condition names produce the cleanest conditional logic:
*> Instead of:
IF WS-EOF-FLAG = 'Y'
*> Use:
IF WS-EOF
*> Instead of:
IF WS-STATUS-CODE = 'AC' OR WS-STATUS-CODE = 'TR'
*> Use (with appropriate 88-levels defined):
IF WS-ACCOUNT-ACTIVEEVALUATE Statement
EVALUATE is COBOL's case/switch construct, but significantly more powerful than most languages' switch statements.
EVALUATE with a Single Subject
EVALUATE WS-TRANSACTION-CODE
WHEN 'DP'
PERFORM PROCESS-DEPOSIT
WHEN 'WD'
PERFORM PROCESS-WITHDRAWAL
WHEN 'TF'
PERFORM PROCESS-TRANSFER
WHEN 'IQ'
PERFORM PROCESS-INQUIRY
WHEN OTHER
PERFORM HANDLE-UNKNOWN-CODE
END-EVALUATE.WHEN OTHER catches anything not matched by preceding WHEN clauses. Unlike C/Java switch, there is no fall-through — each WHEN clause executes independently and control exits the EVALUATE after the first match.
EVALUATE TRUE (Condition-Based)
EVALUATE TRUE evaluates each WHEN clause as a Boolean condition:
EVALUATE TRUE
WHEN WS-AMOUNT < 0
PERFORM HANDLE-NEGATIVE-AMOUNT
WHEN WS-AMOUNT = 0
PERFORM HANDLE-ZERO-AMOUNT
WHEN WS-AMOUNT <= 1000
PERFORM HANDLE-SMALL-AMOUNT
WHEN WS-AMOUNT <= 10000
PERFORM HANDLE-MEDIUM-AMOUNT
WHEN OTHER
PERFORM HANDLE-LARGE-AMOUNT
END-EVALUATE.This is the preferred replacement for an IF/ELSE IF chain with unrelated conditions.
EVALUATE with Ranges and ALSO
EVALUATE WS-SCORE
WHEN 90 THRU 100
MOVE 'A' TO WS-GRADE
WHEN 80 THRU 89
MOVE 'B' TO WS-GRADE
WHEN 70 THRU 79
MOVE 'C' TO WS-GRADE
WHEN 60 THRU 69
MOVE 'D' TO WS-GRADE
WHEN OTHER
MOVE 'F' TO WS-GRADE
END-EVALUATE.The ALSO clause evaluates multiple subjects simultaneously — a decision table in one statement:
EVALUATE WS-ACCOUNT-TYPE ALSO WS-CUSTOMER-CLASS
WHEN 'SAV' ALSO 'PREM'
PERFORM APPLY-PREMIUM-SAVINGS-RATE
WHEN 'SAV' ALSO ANY
PERFORM APPLY-STANDARD-SAVINGS-RATE
WHEN 'CHK' ALSO 'PREM'
PERFORM APPLY-PREMIUM-CHECKING-TERMS
WHEN 'CHK' ALSO ANY
PERFORM APPLY-STANDARD-CHECKING-TERMS
WHEN OTHER ALSO ANY
PERFORM HANDLE-UNKNOWN-ACCOUNT
END-EVALUATE.ANY in a WHEN clause is a wildcard — it matches any value for that subject.
PERFORM Statement
PERFORM has four distinct forms: out-of-line paragraph call, inline loop, UNTIL loop, and VARYING loop.
Out-of-Line PERFORM
Calls a named paragraph or section and returns:
PERFORM INITIALIZE-PROGRAM.
PERFORM READ-NEXT-RECORD.
PERFORM VALIDATE-INPUT-DATA.
PERFORM WRITE-OUTPUT-RECORD.The called paragraph can itself PERFORM other paragraphs. This is the backbone of structured COBOL design: a main-logic paragraph that reads like a table of contents.
PERFORM THRU
Executes a range of paragraphs from the first through the last:
PERFORM CALC-TAX THRU CALC-TAX-EXIT.The EXIT paragraph is typically empty — just a named target:
CALC-TAX.
IF WS-TAXABLE
COMPUTE WS-TAX ROUNDED = WS-AMOUNT * WS-TAX-RATE
ELSE
MOVE ZERO TO WS-TAX
END-IF.
CALC-TAX-EXIT.
EXIT.PERFORM THRU is common in older code to use GO TO ... CALC-TAX-EXIT for early exits from complex paragraph sequences. Modern code prefers END-IF/END-PERFORM scope terminators and rarely needs THRU.
PERFORM ... N TIMES
Executes a paragraph or inline block a fixed number of times:
PERFORM 5 TIMES
DISPLAY 'RETRY ' WS-RETRY-COUNT
PERFORM ATTEMPT-CONNECTION
ADD 1 TO WS-RETRY-COUNT
END-PERFORM.
PERFORM PRINT-PAGE-HEADER 3 TIMES.PERFORM UNTIL
The fundamental loop construct. Tests the condition before each iteration (TEST BEFORE, the default):
PERFORM UNTIL WS-EOF
PERFORM READ-NEXT-RECORD
IF NOT WS-EOF
PERFORM PROCESS-RECORD
END-IF
END-PERFORM.TEST AFTER makes it a do-while — the body executes at least once:
PERFORM WITH TEST AFTER UNTIL WS-VALID-INPUT
PERFORM GET-USER-INPUT
PERFORM VALIDATE-INPUT
END-PERFORM.PERFORM VARYING
PERFORM VARYING is the COBOL for-loop with explicit index, start, step, and limit:
PERFORM VARYING WS-INDEX FROM 1 BY 1
UNTIL WS-INDEX > WS-ARRAY-SIZE
MOVE WS-TABLE-ENTRY(WS-INDEX) TO WS-WORK-ITEM
PERFORM PROCESS-WORK-ITEM
END-PERFORM.The FROM value is the start, BY is the increment (can be negative for counting down), and UNTIL is the termination condition (tested before each iteration by default).
Nested VARYING iterates two indexes simultaneously — essential for two-dimensional table processing:
PERFORM VARYING WS-ROW FROM 1 BY 1
UNTIL WS-ROW > WS-MAX-ROWS
AFTER WS-COL FROM 1 BY 1
UNTIL WS-COL > WS-MAX-COLS
MOVE WS-MATRIX(WS-ROW WS-COL) TO WS-CELL-VALUE
PERFORM PROCESS-CELL
END-PERFORM.The AFTER phrase introduces the inner loop variable. The outer loop increments only when the inner loop completes.
Structured Programming Patterns
Main-Logic Pattern
Every production COBOL program should have a single top-level paragraph that reads like a specification:
PROCEDURE DIVISION.
MAIN-LOGIC.
PERFORM OPEN-FILES
PERFORM READ-FIRST-RECORD
PERFORM PROCESS-ALL-RECORDS UNTIL WS-EOF
PERFORM WRITE-SUMMARY-TOTALS
PERFORM CLOSE-FILES
STOP RUN.
OPEN-FILES.
OPEN INPUT CUSTOMER-FILE
OPEN OUTPUT REPORT-FILE
IF NOT WS-INPUT-OK
PERFORM ABEND-OPEN-FAILURE
END-IF.
READ-FIRST-RECORD.
READ CUSTOMER-FILE INTO WS-CUSTOMER-REC
AT END SET WS-EOF TO TRUE
END-READ.
PROCESS-ALL-RECORDS.
PERFORM VALIDATE-CUSTOMER-RECORD
IF WS-RECORD-VALID
PERFORM GENERATE-REPORT-LINE
ADD 1 TO WS-RECORDS-PROCESSED
ELSE
ADD 1 TO WS-RECORDS-SKIPPED
END-IF
PERFORM READ-NEXT-RECORD.
READ-NEXT-RECORD.
READ CUSTOMER-FILE INTO WS-CUSTOMER-REC
AT END SET WS-EOF TO TRUE
END-READ.Early Exit Pattern
When a paragraph needs to exit early from a loop based on a condition:
SEARCH-LOOP.
PERFORM VARYING WS-IDX FROM 1 BY 1
UNTIL WS-IDX > WS-TABLE-SIZE
OR WS-FOUND
IF WS-TABLE-KEY(WS-IDX) = WS-SEARCH-KEY
SET WS-FOUND TO TRUE
MOVE WS-IDX TO WS-FOUND-INDEX
END-IF
END-PERFORM.Setting a flag (WS-FOUND) as part of the UNTIL condition is the clean way to early-exit a PERFORM VARYING loop without GO TO.
Error Handling Pattern
PROCESS-TRANSACTION.
PERFORM VALIDATE-AMOUNT
IF WS-ERROR-FOUND
PERFORM LOG-VALIDATION-ERROR
GO TO PROCESS-TRANSACTION-EXIT
END-IF
PERFORM APPLY-TRANSACTION
IF WS-ERROR-FOUND
PERFORM LOG-APPLY-ERROR
PERFORM ROLLBACK-TRANSACTION
GO TO PROCESS-TRANSACTION-EXIT
END-IF
PERFORM CONFIRM-TRANSACTION.
PROCESS-TRANSACTION-EXIT.
EXIT.The GO TO ... EXIT pattern is one of the few accepted uses of GO TO in modern COBOL — it provides a clean single-exit point from a complex paragraph.
Conclusion
Mastering IF, EVALUATE, and PERFORM gives you complete control over COBOL program logic. Use EVALUATE in preference to nested IF chains for three or more conditions, use PERFORM to keep paragraphs short and focused, and always use scope terminators (END-IF, END-EVALUATE, END-PERFORM) to make the block structure explicit and unambiguous.
The next topic is string manipulation — STRING, UNSTRING, INSPECT, and the intrinsic FUNCTION catalogue. See COBOL String Handling, or browse the full COBOL Mastery course.
