COBOL (Common Business-Oriented Language) is a compiled English-like computer programming language designed for business use. It is an imperative, procedural language that was designed in 1959.
Use this file to discover all available pages before exploring further.
COBOL Anti-Patterns Overview
COBOL, despite its longevity and importance in business applications, has several common anti-patterns that can lead to maintainability problems and bugs. Here are the most important anti-patterns to avoid when writing COBOL code.
Using GO TO Excessively
* Anti-pattern: Excessive use of GO TOPROCEDURE DIVISION. PERFORM INITIALIZE-ROUTINE. GO TO PROCESS-DATA.INITIALIZE-ROUTINE. MOVE ZEROS TO TOTAL-AMOUNT. MOVE SPACES TO ERROR-MESSAGE.PROCESS-DATA. READ INPUT-FILE AT END GO TO END-OF-FILE END-READ. IF AMOUNT-FIELD > 1000 GO TO LARGE-AMOUNT-ROUTINE ELSE GO TO SMALL-AMOUNT-ROUTINE END-IF.LARGE-AMOUNT-ROUTINE. ADD AMOUNT-FIELD TO LARGE-TOTAL. GO TO PROCESS-DATA.SMALL-AMOUNT-ROUTINE. ADD AMOUNT-FIELD TO SMALL-TOTAL. GO TO PROCESS-DATA.END-OF-FILE. DISPLAY "Processing complete". STOP RUN.* Better approach: Structured programmingPROCEDURE DIVISION. PERFORM INITIALIZE-ROUTINE. PERFORM PROCESS-DATA UNTIL END-OF-FILE. DISPLAY "Processing complete". STOP RUN.INITIALIZE-ROUTINE. MOVE ZEROS TO TOTAL-AMOUNT. MOVE SPACES TO ERROR-MESSAGE.PROCESS-DATA. READ INPUT-FILE AT END SET END-OF-FILE TO TRUE NOT AT END PERFORM PROCESS-RECORD END-READ.PROCESS-RECORD. IF AMOUNT-FIELD > 1000 PERFORM LARGE-AMOUNT-ROUTINE ELSE PERFORM SMALL-AMOUNT-ROUTINE END-IF.LARGE-AMOUNT-ROUTINE. ADD AMOUNT-FIELD TO LARGE-TOTAL.SMALL-AMOUNT-ROUTINE. ADD AMOUNT-FIELD TO SMALL-TOTAL.
Avoid excessive use of GO TO statements, which create spaghetti code that is difficult to understand and maintain. Use structured programming constructs like PERFORM, IF-ELSE, and EVALUATE instead.
Using Magic Numbers
* Anti-pattern: Using magic numbersIF CUSTOMER-TYPE = 1 COMPUTE DISCOUNT = TOTAL-AMOUNT * 0.10ELSE IF CUSTOMER-TYPE = 2 COMPUTE DISCOUNT = TOTAL-AMOUNT * 0.15ELSE IF CUSTOMER-TYPE = 3 COMPUTE DISCOUNT = TOTAL-AMOUNT * 0.20END-IF.* Better approach: Use named constants01 DISCOUNT-RATES. 05 REGULAR-CUSTOMER-RATE PIC 9V99 VALUE 0.10. 05 PREFERRED-CUSTOMER-RATE PIC 9V99 VALUE 0.15. 05 PREMIUM-CUSTOMER-RATE PIC 9V99 VALUE 0.20.01 CUSTOMER-TYPES. 05 REGULAR-CUSTOMER PIC 9 VALUE 1. 05 PREFERRED-CUSTOMER PIC 9 VALUE 2. 05 PREMIUM-CUSTOMER PIC 9 VALUE 3.* In PROCEDURE DIVISIONIF CUSTOMER-TYPE = REGULAR-CUSTOMER COMPUTE DISCOUNT = TOTAL-AMOUNT * REGULAR-CUSTOMER-RATEELSE IF CUSTOMER-TYPE = PREFERRED-CUSTOMER COMPUTE DISCOUNT = TOTAL-AMOUNT * PREFERRED-CUSTOMER-RATEELSE IF CUSTOMER-TYPE = PREMIUM-CUSTOMER COMPUTE DISCOUNT = TOTAL-AMOUNT * PREMIUM-CUSTOMER-RATEEND-IF.
Avoid using magic numbers (hardcoded numeric literals) in your code. Use named constants to make your code more readable and maintainable.
Use hierarchical data structures to organize related data fields. This makes your code more readable and easier to maintain, especially when passing data between program modules.
Hardcoding File Names
* Anti-pattern: Hardcoded file namesSELECT CUSTOMER-FILE ASSIGN TO "C:\DATA\CUSTOMER.DAT" ORGANIZATION IS INDEXED ACCESS MODE IS DYNAMIC RECORD KEY IS CUSTOMER-ID.* Better approach: Use configuration files or environment variablesSELECT CUSTOMER-FILE ASSIGN TO CUSTOMER-FILE-PATH ORGANIZATION IS INDEXED ACCESS MODE IS DYNAMIC RECORD KEY IS CUSTOMER-ID.* In WORKING-STORAGE SECTION01 CONFIGURATION-VARIABLES. 05 CUSTOMER-FILE-PATH PIC X(100).* In PROCEDURE DIVISIONACCEPT CUSTOMER-FILE-PATH FROM ENVIRONMENT "CUSTOMER_FILE_PATH".IF CUSTOMER-FILE-PATH = SPACES MOVE "C:\DATA\CUSTOMER.DAT" TO CUSTOMER-FILE-PATHEND-IF.
Avoid hardcoding file paths and names in your programs. Use configuration files or environment variables instead, which makes your code more flexible and portable across different environments.
Not Validating Input Data
* Anti-pattern: Not validating inputACCEPT CUSTOMER-ID.READ CUSTOMER-FILE INVALID KEY DISPLAY "Customer not found"END-READ.* Better approach: Validate input dataACCEPT CUSTOMER-ID.* Validate input formatIF CUSTOMER-ID IS NOT NUMERIC DISPLAY "Error: Customer ID must be numeric" EXIT PARAGRAPHEND-IF.IF CUSTOMER-ID = ZEROS DISPLAY "Error: Customer ID cannot be zero" EXIT PARAGRAPHEND-IF.* Proceed with valid inputREAD CUSTOMER-FILE INVALID KEY DISPLAY "Customer not found"END-READ.
Always validate input data before processing it. This helps prevent runtime errors, data corruption, and potential security issues.
Using PERFORM THRU
* Anti-pattern: Using PERFORM THRUPROCEDURE DIVISION. PERFORM INITIALIZE-ROUTINE THRU INITIALIZE-EXIT. PERFORM PROCESS-DATA THRU PROCESS-EXIT. STOP RUN.INITIALIZE-ROUTINE. MOVE ZEROS TO TOTAL-AMOUNT. MOVE SPACES TO ERROR-MESSAGE.INITIALIZE-EXIT. EXIT.PROCESS-DATA. READ INPUT-FILE AT END GO TO PROCESS-EXIT END-READ. ADD AMOUNT-FIELD TO TOTAL-AMOUNT. GO TO PROCESS-DATA.PROCESS-EXIT. EXIT.* Better approach: Use structured PERFORM statementsPROCEDURE DIVISION. PERFORM INITIALIZE-ROUTINE. PERFORM PROCESS-DATA UNTIL END-OF-FILE. STOP RUN.INITIALIZE-ROUTINE. MOVE ZEROS TO TOTAL-AMOUNT. MOVE SPACES TO ERROR-MESSAGE.PROCESS-DATA. READ INPUT-FILE AT END SET END-OF-FILE TO TRUE NOT AT END ADD AMOUNT-FIELD TO TOTAL-AMOUNT END-READ.
Avoid using PERFORM THRU statements, which can lead to maintenance issues if paragraphs are reordered or renamed. Use structured PERFORM statements with well-defined paragraph boundaries instead.
Not Using COPY Books
* Anti-pattern: Duplicating data definitions* In Program A01 CUSTOMER-RECORD. 05 CUSTOMER-ID PIC X(10). 05 CUSTOMER-NAME PIC X(30). 05 CUSTOMER-ADDRESS. 10 STREET-ADDRESS-1 PIC X(30). 10 STREET-ADDRESS-2 PIC X(30). 10 CITY PIC X(20). 10 STATE PIC X(2). 10 ZIP-CODE PIC X(10).* In Program B (duplicated definition)01 CUSTOMER-RECORD. 05 CUSTOMER-ID PIC X(10). 05 CUSTOMER-NAME PIC X(30). 05 CUSTOMER-ADDRESS. 10 STREET-ADDRESS-1 PIC X(30). 10 STREET-ADDRESS-2 PIC X(30). 10 CITY PIC X(20). 10 STATE PIC X(2). 10 ZIP-CODE PIC X(10).* Better approach: Use COPY books* In CUSTOMER.CPY file01 CUSTOMER-RECORD. 05 CUSTOMER-ID PIC X(10). 05 CUSTOMER-NAME PIC X(30). 05 CUSTOMER-ADDRESS. 10 STREET-ADDRESS-1 PIC X(30). 10 STREET-ADDRESS-2 PIC X(30). 10 CITY PIC X(20). 10 STATE PIC X(2). 10 ZIP-CODE PIC X(10).* In Program A and Program BCOPY CUSTOMER.
Use COPY books to share common data definitions and code across multiple programs. This reduces duplication and ensures consistency.
Use meaningful and descriptive names for variables, paragraphs, and sections. This makes your code more readable and easier to understand and maintain.
Not Using Proper Error Handling
* Anti-pattern: Minimal error handlingREAD CUSTOMER-FILE INVALID KEY DISPLAY "Error reading file"END-READ.* Better approach: Comprehensive error handlingREAD CUSTOMER-FILE INVALID KEY MOVE "Y" TO ERROR-FOUND MOVE "Customer record not found" TO ERROR-MESSAGE PERFORM LOG-ERROR PERFORM DISPLAY-ERROR-TO-USEREND-READ.IF ERROR-FOUND = "Y" PERFORM ERROR-RECOVERY-ROUTINEEND-IF.
Implement proper error handling with detailed error messages, logging, and recovery procedures. This helps diagnose and resolve issues more quickly.
Using REDEFINES Inappropriately
* Anti-pattern: Inappropriate use of REDEFINES01 MULTI-PURPOSE-FIELD. 05 NUMERIC-VALUE PIC 9(10). 05 TEXT-VALUE REDEFINES NUMERIC-VALUE PIC X(10). 05 DATE-VALUE REDEFINES NUMERIC-VALUE. 10 YEAR PIC 9(4). 10 MONTH PIC 9(2). 10 DAY PIC 9(2). 10 FILLER PIC 9(2).* Better approach: Use separate fields for different purposes01 CUSTOMER-DATA. 05 CUSTOMER-ID PIC 9(10). 05 CUSTOMER-NAME PIC X(30). 05 REGISTRATION-DATE. 10 REG-YEAR PIC 9(4). 10 REG-MONTH PIC 9(2). 10 REG-DAY PIC 9(2).
Avoid using REDEFINES to repurpose the same memory location for different data types. This can lead to data corruption and maintenance issues. Use separate fields for different purposes instead.
Not Using Structured Programming
* Anti-pattern: Unstructured codePROCEDURE DIVISION.MAIN-LOGIC. OPEN INPUT CUSTOMER-FILE. OPEN OUTPUT REPORT-FILE. READ CUSTOMER-FILE AT END MOVE "Y" TO END-OF-FILE-FLAG END-READ. PERFORM UNTIL END-OF-FILE-FLAG = "Y" MOVE CUSTOMER-NAME TO REPORT-NAME MOVE CUSTOMER-BALANCE TO REPORT-BALANCE WRITE REPORT-RECORD READ CUSTOMER-FILE AT END MOVE "Y" TO END-OF-FILE-FLAG END-READ END-PERFORM. CLOSE CUSTOMER-FILE. CLOSE REPORT-FILE. STOP RUN.* Better approach: Structured code with modular designPROCEDURE DIVISION.MAIN-LOGIC. PERFORM INITIALIZATION. PERFORM PROCESS-RECORDS UNTIL END-OF-FILE. PERFORM TERMINATION. STOP RUN.INITIALIZATION. OPEN INPUT CUSTOMER-FILE. OPEN OUTPUT REPORT-FILE. PERFORM READ-CUSTOMER-RECORD.PROCESS-RECORDS. PERFORM WRITE-REPORT-RECORD. PERFORM READ-CUSTOMER-RECORD.WRITE-REPORT-RECORD. MOVE CUSTOMER-NAME TO REPORT-NAME. MOVE CUSTOMER-BALANCE TO REPORT-BALANCE. WRITE REPORT-RECORD.READ-CUSTOMER-RECORD. READ CUSTOMER-FILE AT END MOVE "Y" TO END-OF-FILE-FLAG END-READ.TERMINATION. CLOSE CUSTOMER-FILE. CLOSE REPORT-FILE.
Use structured programming principles with modular design. Break down your program into logical, self-contained modules with clear responsibilities.
Not Using EVALUATE for Complex Conditions
* Anti-pattern: Nested IF statementsIF TRANSACTION-TYPE = "S" IF CUSTOMER-TYPE = "R" IF AMOUNT > 1000 COMPUTE DISCOUNT = AMOUNT * 0.10 ELSE COMPUTE DISCOUNT = AMOUNT * 0.05 END-IF ELSE IF CUSTOMER-TYPE = "P" IF AMOUNT > 1000 COMPUTE DISCOUNT = AMOUNT * 0.15 ELSE COMPUTE DISCOUNT = AMOUNT * 0.10 END-IF END-IFEND-IF.* Better approach: Use EVALUATEEVALUATE TRUE WHEN TRANSACTION-TYPE = "S" AND CUSTOMER-TYPE = "R" AND AMOUNT > 1000 COMPUTE DISCOUNT = AMOUNT * 0.10 WHEN TRANSACTION-TYPE = "S" AND CUSTOMER-TYPE = "R" COMPUTE DISCOUNT = AMOUNT * 0.05 WHEN TRANSACTION-TYPE = "S" AND CUSTOMER-TYPE = "P" AND AMOUNT > 1000 COMPUTE DISCOUNT = AMOUNT * 0.15 WHEN TRANSACTION-TYPE = "S" AND CUSTOMER-TYPE = "P" COMPUTE DISCOUNT = AMOUNT * 0.10 WHEN OTHER MOVE ZEROS TO DISCOUNTEND-EVALUATE.
Use the EVALUATE statement for complex conditional logic instead of deeply nested IF statements. This makes your code more readable and easier to maintain.