Skip to content
PYD ENTERPRISES

COBOL anti-pattern

GO TO DEPENDING ON

A computed jump table that transfers control to the Nth paragraph in a list, and silently falls through to the next statement when the index is out of range.

FAMILY
COBOL
CUTOVER RISK
High
Hides business logic or coupling that a rewrite will drop unless it is extracted explicitly.
MODERN MAPPING
Dispatch table with explicit default
GENERATED ARTIFACT
TXNROUTE.routing.dmn.json

Before and after

legacy source → modern mapping

Legacy: exhibits the pattern

TXNROUTE.cblCOBOL
1 01 WS-TXN-INDEX PIC 9(02) COMP.
2 ...
3 1000-ROUTE-TRANSACTION.
4 EVALUATE TXN-CODE
5 WHEN 'DEP' MOVE 1 TO WS-TXN-INDEX
6 WHEN 'WDL' MOVE 2 TO WS-TXN-INDEX
7 WHEN 'XFR' MOVE 3 TO WS-TXN-INDEX
8 WHEN 'FEE' MOVE 4 TO WS-TXN-INDEX
9 WHEN OTHER MOVE 0 TO WS-TXN-INDEX
10 END-EVALUATE
11 GO TO 2100-DEPOSIT
12 2200-WITHDRAWAL
13 2300-TRANSFER
14 2400-FEE-ASSESS
15 DEPENDING ON WS-TXN-INDEX.
16 * INDEX 0: FALLS THROUGH TO SUSPENSE POSTING
17 PERFORM 2900-SUSPENSE-POST
18 GO TO 1999-EXIT.

Modern: Dispatch table with explicit default

routeTransaction.tsTypeScript
1type TxnCode = "DEP" | "WDL" | "XFR" | "FEE";
2
3const handlers: Record<TxnCode, (t: Txn) => Result> = {
4 DEP: postDeposit, // 2100-DEPOSIT
5 WDL: postWithdrawal, // 2200-WITHDRAWAL
6 XFR: postTransfer, // 2300-TRANSFER
7 FEE: assessFee, // 2400-FEE-ASSESS
8};
9
10export function routeTransaction(t: Txn): Result {
11 const handler = handlers[t.code as TxnCode];
12 if (handler === undefined) {
13 // Legacy behavior: out-of-range index fell through
14 // to 2900-SUSPENSE-POST, it did not abend.
15 return postToSuspense(t);
16 }
17 return handler(t);
18}

Why it is dangerous in modernization

GO TO DEPENDING ON packs a routing decision into one line: the value of an integer selects one of several paragraph names. The business meaning of each index value usually lives somewhere else, in a transaction code table, a copybook comment, or nowhere. The worst behavior is the out-of-range case. When the identifier is zero, negative, or larger than the number of procedure names, COBOL ignores the GO TO entirely and continues with the next statement. Many programs rely on that as an implicit default branch without saying so. A rewrite that turns the table into a switch with an exception on the default case changes the behavior for exactly the records that nobody tested.

How PYD ENTERPRISES detects it

The parser emits a multi-way branch node with one edge per listed procedure name, labelled with its 1-based position, plus an explicit fall-through edge for the out-of-range case. Value-range analysis on the selector variable then works backwards through MOVE, COMPUTE, SEARCH and 88-level SET statements to determine which index values can actually reach the jump. Unreachable entries and a reachable fall-through edge are both reported as findings, and the selector's upstream definitions are traced to the input field or table that feeds it.

Maps to: Dispatch table with explicit default

Map the jump table to an enum keyed dispatch (a switch expression in Java or C#, a map of handlers in TypeScript or Go) and make the default branch explicit. When the selector is derived from a business code such as a transaction type, the routing itself is a decision and belongs in a DMN table with a FIRST or UNIQUE hit policy, so analysts can review which codes reach which handler.

Generated specification fragment

TXNROUTE.routing.dmn.jsonJSON
1{
2 "decision": "Route transaction",
3 "hitPolicy": "UNIQUE",
4 "inputs": ["txnCode"],
5 "outputs": ["handler"],
6 "rules": [
7 { "id": "R1", "txnCode": "\"DEP\"", "handler": "deposit", "source": "TXNROUTE.cbl:5" },
8 { "id": "R2", "txnCode": "\"WDL\"", "handler": "withdrawal", "source": "TXNROUTE.cbl:6" },
9 { "id": "R3", "txnCode": "\"XFR\"", "handler": "transfer", "source": "TXNROUTE.cbl:7" },
10 { "id": "R4", "txnCode": "\"FEE\"", "handler": "feeAssess", "source": "TXNROUTE.cbl:8" },
11 { "id": "R5", "txnCode": "-", "handler": "suspense", "source": "TXNROUTE.cbl:9, 17",
12 "note": "implicit fall-through when index is out of range" }
13 ]
14}

Every generated element links back to the source file and line range shown in the legacy pane. Review and sign off before building against it.

The fall-through rule, precisely

The COBOL standard and IBM Enterprise COBOL define the semantics the same way: if the value of the DEPENDING ON identifier is 1, control goes to the first procedure name; if it is 2, to the second; and so on. If the value is outside the range 1 to n, where n is the number of procedure names, the GO TO is not executed and control passes to the next statement in sequence. There is no runtime error and no condition raised.

In the example above the author used that rule on purpose: the WHEN OTHER branch sets the index to zero so that unknown codes fall through to suspense posting. In older code the zero case is often an accident, for instance a selector loaded from a packed field that can arrive as zero when an upstream file has a blank record. Both cases look identical in the source, which is why the engine reports the fall-through edge as reachable and asks a reviewer to classify it.

When the table is also a business rule

A jump table keyed by transaction type, product code or claim category is routing logic that the business owns. Leaving it as a switch statement inside a service hides it again. PYD ENTERPRISES exports the selector derivation and the jump targets together as one decision table, so the analyst who signs off fee assessment can also see that a new code value, added in the channel layer but not in the mainframe table, would land in suspense posting rather than fail.

Testing the conversion

Generate one equivalence fixture per index value, one for zero, and one for n plus 1. The last two are the cases most often lost. If value-range analysis proves the selector can never exceed n, the engine marks the n plus 1 fixture as unreachable instead of emitting it, and records the proof steps so the reviewer can check them.

Find every GO TO DEPENDING ON in your codebase.

Run PYD ENTERPRISES in your private VPC and get each occurrence with file, line range, modern mapping and equivalence fixtures, ready for your architects to review.