Case Study 1: Restructuring Legacy Spaghetti Code at Metro Transit Authority

Background

The Metro Transit Authority (MTA) operates the public transportation system for a metropolitan area serving 2.3 million daily riders. Their fare collection system runs on an IBM z/OS mainframe and processes approximately 4.5 million transactions daily through a suite of COBOL batch programs.

The central program in this suite is FARCALC -- the Fare Calculation Program. Originally written in 1987, FARCALC has been modified by dozens of programmers over nearly four decades. At the time of this case study (2025), the program had grown to 10,247 lines and had become what the maintenance team called "the monster."

The Problem

Maria Santos, a senior COBOL developer, was assigned to add support for a new contactless payment system. After three days of studying the code, she filed the following incident report:

Subject: FARCALC is unmaintainable in its current state

After reviewing the FARCALC source, I believe any modification carries an unacceptable risk of introducing defects. The program has the following structural problems:

  1. The IDENTIFICATION DIVISION has no AUTHOR or DATE-WRITTEN paragraphs. The only comment in the entire program says "FARE CALC" on line 1.

  2. The ENVIRONMENT DIVISION references a SOURCE-COMPUTER of "IBM-370" that has not existed in our shop since 1998.

  3. The DATA DIVISION contains 847 data items in WORKING-STORAGE, many with cryptic one- to three-character names like X1, FL, T3, and AMT. There are no comments explaining what any of them represent. Seventeen items are named FILLER but are not actually filler -- they are used in the PROCEDURE DIVISION.

  4. The PROCEDURE DIVISION is a single section with 43 paragraphs, but the paragraphs have no numeric prefixes and no consistent naming convention. Names include DO-IT, NEXT-PART, FIX-IT, CALC-1, CALC-2, CALC-2A, CALC-2B, and MORE-STUFF.

  5. There are no scope terminators anywhere. All conditional logic uses periods. I counted 17 nested IF statements (deepest nesting: 9 levels) with periods at various levels. I cannot determine which statements are inside which IF blocks.

  6. Fourteen GO TO statements jump between paragraphs in no apparent pattern. The paragraph FIX-IT is reached from six different GO TO statements.

  7. There is no file status checking. If a file fails to open, the program continues with unpredictable results.

  8. The same block of code for reading the fare table file appears in five different places, copy-pasted with slight variations.

I recommend a complete structural refactoring before any new features are added.

The Diagnosis

Maria worked with her team lead, David Park, to create a structural map of the existing program. They identified the following issues categorized by division:

IDENTIFICATION DIVISION Issues

  • No documentation whatsoever
  • Program name FARCALC gave no indication it was part of the fare collection suite
  • No version tracking or modification history

ENVIRONMENT DIVISION Issues

  • Obsolete computer names
  • No SPECIAL-NAMES definitions, despite the program needing to handle currency formatting
  • File assignments used hard-coded DD names without comments explaining what each file contained
  • No FILE STATUS clauses on any of the seven files

DATA DIVISION Issues

  • 847 WORKING-STORAGE items with no grouping structure (all were 77-level or ungrouped 01-level)
  • No naming convention: X1, FL, CTR1, CTR2, CTR3 alongside FARE-AMOUNT and PASSENGER-COUNT
  • Three separate date fields with identical layouts but different names, no REDEFINES
  • Twelve unused variables (dead code) that had accumulated over 38 years
  • Condition names (88-levels) existed for only 2 of the 15 flag variables
  • Report lines were built using STRING instead of structured group items

PROCEDURE DIVISION Issues

  • 43 paragraphs in a single, flat list (no sections)
  • No consistent naming convention
  • 14 GO TO statements creating "spaghetti" control flow
  • 17 deeply nested IF blocks relying on periods for scope termination
  • Five copies of the same file-read logic with slight variations
  • No error handling paragraphs
  • No consistent initialization or finalization logic
  • The "main" paragraph was named BEGIN and was at the bottom of the program

The Refactoring Plan

The team developed a phased approach:

Phase 1: Documentation and Understanding (2 weeks)

Before changing a single line of code, the team:

  1. Added a comprehensive comment block to the IDENTIFICATION DIVISION documenting the program's purpose, its place in the system, and known issues
  2. Traced every data item in WORKING-STORAGE and added comments explaining each one
  3. Created a control flow diagram showing all paragraph connections and GO TO paths
  4. Built a test suite of 200 production-like transactions with known expected results

Phase 2: Data Division Restructuring (3 weeks)

  1. Renamed all cryptic variables using the WS- prefix convention. Created a mapping document showing old name to new name.
Old Name New Name Purpose
X1 WS-ZONE-CODE Transit zone identifier
FL WS-FARE-TYPE-FLAG Flag: regular/reduced/free
T3 WS-TRANSFER-COUNT Number of transfers used
AMT WS-BASE-FARE-AMOUNT Base fare before adjustments
CTR1 WS-RECORD-COUNT Total records processed
  1. Grouped related items under meaningful 01-level group names: ```cobol * BEFORE: 847 flat items 77 X1 PIC XX. 77 FL PIC X. 77 T3 PIC 9. 77 AMT PIC 9(3)V99.

      * AFTER: Organized groups
       01  WS-FARE-CALCULATION-FIELDS.
           05  WS-ZONE-CODE          PIC XX.
           05  WS-FARE-TYPE-FLAG     PIC X.
               88  REGULAR-FARE      VALUE 'R'.
               88  REDUCED-FARE      VALUE 'D'.
               88  FREE-FARE         VALUE 'F'.
           05  WS-TRANSFER-COUNT     PIC 9.
           05  WS-BASE-FARE-AMOUNT   PIC 9(3)V99.
    

    ```

  2. Added condition names (88-levels) for all flag variables

  3. Removed 12 dead variables after confirming they were unreferenced
  4. Created structured report lines replacing inline STRING construction
  5. Added FILE STATUS variables for all seven files

Phase 3: Procedure Division Restructuring (4 weeks)

This was the most challenging and risky phase.

  1. Eliminated GO TO statements by restructuring the control flow. Each GO TO was analyzed: - Forward GO TOs (jumping ahead in the same logical path) were replaced with PERFORM and condition checks - Backward GO TOs (creating loops) were replaced with PERFORM UNTIL constructs - GO TOs that jumped into error handling were replaced with PERFORM of error paragraphs

  2. Created proper sections with the numeric naming convention: 0000-MAIN-CONTROL SECTION 1000-INITIALIZATION SECTION 2000-PROCESS-TRANSACTIONS SECTION 2100-CALCULATE-BASE-FARE SECTION 2200-APPLY-TRANSFER-DISCOUNT SECTION 2300-APPLY-TIME-DISCOUNT SECTION 2400-APPLY-ZONE-SURCHARGE SECTION 3000-REPORT-GENERATION SECTION 4000-FINALIZATION SECTION 8000-FILE-IO-ROUTINES SECTION 9000-ERROR-HANDLING SECTION

  3. Replaced periods with scope terminators throughout. Every IF got an END-IF, every READ got an END-READ, every PERFORM got proper scope.

  4. Consolidated duplicate code -- the five copies of the fare-table read logic became a single paragraph 8100-READ-FARE-TABLE called from five places.

  5. Added comprehensive error handling -- the 9000-ERROR-HANDLING section handled file errors, data validation errors, and unexpected conditions with meaningful diagnostic messages.

Phase 4: Validation (2 weeks)

  1. Ran the 200-transaction test suite against both the old and new versions
  2. Compared output byte-for-byte
  3. Ran a full month of production data through both versions in parallel
  4. Had two independent reviewers check the refactored code

The Results

Before Refactoring

Metric Value
Total lines 10,247
Comment lines 1
Comment ratio 0.01%
WORKING-STORAGE items 847
Items with meaningful names ~120
GO TO statements 14
Deepest IF nesting 9 levels
Sections 0
Named paragraphs 43
Duplicate code blocks 5
FILE STATUS checks 0
Average time to add a feature 3 weeks
Defects introduced per modification 2.1

After Refactoring

Metric Value
Total lines 8,432
Comment lines 614
Comment ratio 7.3%
WORKING-STORAGE items 783
Items with meaningful names 783 (100%)
GO TO statements 0
Deepest IF nesting 3 levels
Sections 11
Named paragraphs 47
Duplicate code blocks 0
FILE STATUS checks 21 (all operations)
Average time to add a feature 4 days
Defects introduced per modification 0.3

The program was actually shorter after refactoring despite adding 614 comment lines, because the elimination of duplicate code, dead variables, and convoluted logic removed nearly 2,500 lines.

Coding Standards Established

As a result of this experience, the MTA development team established formal coding standards:

  1. All programs must have a complete IDENTIFICATION DIVISION with PROGRAM-ID, AUTHOR, and DATE-WRITTEN at minimum, plus a comment block describing the program's purpose and its role in the application suite.

  2. All data names must use the section-prefix convention (WS- for WORKING-STORAGE, LS- for LINKAGE SECTION, etc.) and must be descriptive English words.

  3. All flag variables must have 88-level condition names.

  4. No GO TO statements are permitted except in very specific, documented patterns (such as GO TO in a SORT INPUT PROCEDURE).

  5. Scope terminators are required for all conditional statements. The period-only style is prohibited.

  6. The numeric paragraph naming convention (0000-9999 ranges) is mandatory.

  7. Every section must have an EXIT paragraph.

  8. FILE STATUS must be declared and checked for every file on every I/O operation.

  9. No paragraph may exceed 50 lines. If it does, it must be broken into smaller paragraphs.

  10. Comments are required for every section header, every paragraph header, and any logic that is not immediately obvious.

Lessons Learned

Maria Santos' final report included these observations:

The refactoring of FARCALC took 11 weeks of effort by two senior developers. The original program took perhaps two weeks to write in 1987. The additional 11 weeks represent the accumulated cost of 38 years of unstructured modifications.

If the original programmer had followed the division and naming conventions that COBOL was designed to support, most of these problems would never have existed. The four-division structure is not just a language requirement -- it is an invitation to write well-organized code. When that invitation is declined, the maintenance cost grows exponentially.

The most important lesson: structure is not overhead. Structure is the foundation that makes future work possible. Every hour spent organizing code properly saves ten hours of future maintenance.

Discussion Questions

  1. Why did the team create a test suite before making any changes? What would have happened if they refactored first and tested later?

  2. The original program used 77-level items for everything in WORKING-STORAGE. What are the advantages of grouping related items under 01-level group names instead?

  3. The GO TO statement is legal in COBOL, so why did the team prohibit it entirely? Can you think of any situation where a GO TO might be justified?

  4. The refactored program has 47 paragraphs compared to the original 43. How can more paragraphs make a program simpler?

  5. Maria estimated that each modification to the old program introduced an average of 2.1 defects. How does the period-as-scope-terminator contribute to this high defect rate?

  6. The coding standards require that no paragraph exceed 50 lines. What is the rationale for this rule? How does it relate to the section/paragraph structure of the PROCEDURE DIVISION?

  7. Could this refactoring have been done incrementally (one section at a time) rather than all at once? What are the trade-offs of each approach?