Skip to content
PYD ENTERPRISES

COBOL anti-pattern

88-level condition names

Named conditions over a field's values that encode business categories, often with overlapping value lists, where SET name TO TRUE writes only the first value in the list.

FAMILY
COBOL
CUTOVER RISK
Medium
Adds migration effort and contract risk, but behavior is visible in the code once you look.
MODERN MAPPING
Enum with category predicates
GENERATED ARTIFACT
BILLING.dmn.json

Before and after

legacy source → modern mapping

Legacy: exhibits the pattern

POLSTAT.cpyCOBOL
1 05 POL-STATUS PIC X(02).
2 88 POL-ACTIVE VALUE 'AC' 'RN' 'RI'.
3 88 POL-IN-GRACE VALUE 'GR' 'G2'.
4 88 POL-LAPSED VALUE 'LP'.
5 88 POL-TERMINATED VALUE 'TC' 'TN' 'TL'
6 'LP'.
7 88 POL-BILLABLE VALUE 'AC' 'RN' 'RI'
8 'GR' 'G2'.
9 05 INS-AGE PIC 9(03).
10 88 AGE-SENIOR-BAND VALUE 65 THRU 120.
11 ...
12 IF POL-BILLABLE AND NOT AGE-SENIOR-BAND
13 PERFORM 5000-BILL-STANDARD
14 END-IF
15 SET POL-ACTIVE TO TRUE.

Modern: Enum with category predicates

PolicyStatus.javaJava
1public enum PolicyStatus {
2 AC, RN, RI, GR, G2, LP, TC, TN, TL;
3
4 static final EnumSet<PolicyStatus> ACTIVE =
5 EnumSet.of(AC, RN, RI);
6 static final EnumSet<PolicyStatus> TERMINATED =
7 EnumSet.of(TC, TN, TL, LP); // LP overlaps
8 static final EnumSet<PolicyStatus> BILLABLE =
9 EnumSet.of(AC, RN, RI, GR, G2);
10
11 public boolean isBillable() {
12 return BILLABLE.contains(this);
13 }
14
15 /** SET POL-ACTIVE TO TRUE writes 'AC'. */
16 public static PolicyStatus activate() {
17 return AC;
18 }
19}

Why it is dangerous in modernization

An 88-level turns raw codes into business vocabulary: IF POL-BILLABLE reads well, but the definition of billable lives in a VALUE clause in a copybook that dozens of programs share. Value lists overlap, so one status code can make two conditions true at once. SET POL-ACTIVE TO TRUE moves the first value in the list into the parent field, so the code a record ends up with depends on the order of literals in a copybook. Teams that migrate the parent field as a plain string and reimplement each IF by hand drop codes from categories, miss overlaps, and change the stored code when a program sets a condition. These are small mistakes that land in eligibility, billing and collections logic.

How PYD ENTERPRISES detects it

The parser attaches each 88-level to its parent field with its full value set, expanding THRU ranges into intervals. Every condition test and every SET TO TRUE in the procedure division resolves to that parent, so the engine knows which values each branch actually admits and which literal each SET writes. It computes overlaps between conditions on the same field, conditions that no reachable value can satisfy, and raw-literal comparisons on the parent field that bypass the condition names. The value sets then become input entries of the DMN tables that use them.

Maps to: Enum with category predicates

The parent field becomes an enum of its legal codes; each 88-level becomes a named set of enum values with a predicate such as isBillable(). In DMN, the same sets appear as input entries in FEEL list syntax, so business analysts review the category definitions directly. Where the legacy order of a VALUE list decides which code SET TO TRUE writes, the generated spec says so explicitly.

Generated specification fragment

BILLING.dmn.jsonJSON
1{
2 "decision": "Standard billing applies",
3 "hitPolicy": "FIRST",
4 "inputs": ["policyStatus", "insuredAge"],
5 "rules": [
6 {
7 "id": "R1",
8 "policyStatus": "\"AC\",\"RN\",\"RI\",\"GR\",\"G2\"",
9 "insuredAge": "not([65..120])",
10 "output": true,
11 "source": "POLSTAT.cpy:7-8, BILLRUN.cbl:12"
12 },
13 { "id": "R2", "policyStatus": "-", "insuredAge": "-",
14 "output": false }
15 ]
16}

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.

SET TO TRUE and SET TO FALSE

SET condition-name TO TRUE moves the first literal in the VALUE clause into the parent field. For POL-ACTIVE that is 'AC', regardless of whether the policy was a renewal. If a later maintainer reorders the list to 'RN' 'AC' 'RI', every program that sets the condition starts writing 'RN', with no change in the procedure division. SET condition-name TO FALSE is only valid when the 88-level declares a WHEN SET TO FALSE value; without one the compiler rejects it, so older code clears conditions with a MOVE of a literal instead, which the engine also tracks.

Overlaps are business decisions

In the example, 'LP' is both lapsed and terminated. A program that tests POL-TERMINATED first and POL-LAPSED second takes a different branch than one that tests them the other way round. The overlap may be deliberate (lapsed policies are a kind of terminated policy for reporting) or an accident from a code added years later. PYD ENTERPRISES reports each overlap with the programs whose behavior depends on test order, so the definition is decided once, in the specification, instead of separately in each new service.

Raw literal comparisons

Shared copybooks rarely stay the only source of truth. Programs written later compare POL-STATUS = 'GR' directly and forget 'G2', or add a code that no 88-level includes. The engine lists every raw comparison on a field that has condition names, and every literal written to the field that falls outside all of its 88-levels. Those lists are usually the fastest way to find a missing code in a category before it becomes a production defect in the rewritten system.

Find every 88-level condition names 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.