Case Study 1: GlobalBank's Modularization Journey
Background
In 2004, GlobalBank's account maintenance program, ACCT-MAINT, was a single COBOL source file of 11,847 lines. It handled account creation, updates, balance adjustments, interest calculations, fee processing, statement generation, and audit logging — all in one program. Maria Chen had just joined the team as a senior developer.
"My first week, I was asked to modify the interest calculation formula for a new savings product," Maria recalls. "I spent three days just understanding the program's flow. The interest logic was scattered across four different sections, interleaved with account validation and reporting code. When I finally made the change — a two-line modification — regression testing took two weeks because everything was interconnected."
The final straw came during a production incident in March 2005. A change to the fee calculation logic introduced a bug that corrupted account balances for 2,300 customers. The root cause: a paragraph that was shared between the fee calculation and the balance adjustment logic. Fixing the fee calculation inadvertently broke the balance adjustment.
The Problem
Maria documented the issues in a proposal to management:
- Uncontrolled coupling: Data items in WORKING-STORAGE were used by multiple, unrelated functions. Changing one item could break another function.
- No isolation: It was impossible to test the interest calculation without running the entire program, including file I/O, validation, and reporting.
- Single point of failure: Any change to any part of the program required regression testing of the entire system.
- Deployment risk: Every deployment was all-or-nothing. Rolling back a fee change meant rolling back interest changes too.
- Development bottleneck: Only one developer could work on the program at a time due to source management conflicts.
The Solution: Modular Decomposition
Maria proposed decomposing ACCT-MAINT into a driver program with focused subprograms. She identified seven functional areas:
| Module | Responsibility | Lines | Call Type |
|---|---|---|---|
| ACCT-MAINT | Driver/orchestrator | ~800 | N/A (main) |
| ACCTREAD | VSAM account I/O | ~600 | Dynamic |
| ACCTVAL | Account validation | ~1,200 | Dynamic |
| ACCTCALC | Interest/fee calculation | ~1,800 | Dynamic |
| ACCTUPD | Account update/rewrite | ~500 | Dynamic |
| ACCTAUDT | Audit trail writing | ~400 | Dynamic |
| DTEVALID | Date validation | ~300 | Dynamic (shared) |
| ERRLOG | Error logging | ~250 | Dynamic (shared) |
Interface Design
Maria's key design decision was to use a single, well-defined interface area for the account-related modules, defined in a copybook:
*================================================================
* ACCTIFCP — Account Module Interface Copybook
* Used by: ACCT-MAINT (driver), ACCTREAD, ACCTVAL,
* ACCTCALC, ACCTUPD, ACCTAUDT
*================================================================
01 ACCT-INTERFACE-AREA.
05 ACCT-FUNCTION PIC X(4).
88 ACCT-READ VALUE 'READ'.
88 ACCT-WRITE VALUE 'WRIT'.
88 ACCT-UPDATE VALUE 'UPDT'.
88 ACCT-DELETE VALUE 'DELT'.
05 ACCT-DATA.
10 ACCT-ID PIC X(10).
10 ACCT-NAME PIC X(30).
10 ACCT-TYPE PIC X(2).
10 ACCT-BALANCE PIC S9(11)V99 COMP-3.
10 ACCT-INT-RATE PIC S9(3)V9(4) COMP-3.
10 ACCT-STATUS PIC X(1).
10 ACCT-OPEN-DATE PIC 9(8).
10 ACCT-LAST-TXN PIC 9(8).
05 ACCT-RETURN-INFO.
10 ACCT-RETURN-CD PIC S9(4) COMP.
88 ACCT-SUCCESS VALUE 0.
88 ACCT-WARNING VALUE 4.
88 ACCT-ERROR VALUE 8.
88 ACCT-SEVERE VALUE 12.
10 ACCT-MSG-COUNT PIC S9(4) COMP.
10 ACCT-MESSAGES.
15 ACCT-MSG PIC X(80)
OCCURS 10 TIMES.
Migration Strategy
Rather than rewriting everything at once, Maria used a phased approach:
Phase 1 (2 weeks): Extract DTEVALID and ERRLOG as shared utilities. These had the clearest boundaries and the highest reuse potential.
Phase 2 (3 weeks): Extract ACCTREAD and ACCTUPD — the I/O layer. The driver program still contained validation, calculation, and audit logic, but I/O was isolated.
Phase 3 (4 weeks): Extract ACCTVAL and ACCTCALC — the business logic. This was the hardest phase because the validation and calculation logic was interleaved in the original code.
Phase 4 (1 week): Extract ACCTAUDT and clean up the driver program.
Each phase was deployed independently. At each stage, the system was fully functional — a partially modularized system worked just as well as the monolith.
Results
After six months of phased deployment:
| Metric | Before | After | Improvement |
|---|---|---|---|
| Average time to implement a change | 5 days | 1.5 days | 70% |
| Regression test scope per change | Full system | Single module | 85% reduction |
| Production incidents per quarter | 4.2 | 1.1 | 74% reduction |
| Developers who can work simultaneously | 1 | 4-5 | 4-5x |
| Time to onboard new developer | 3 weeks | 1 week | 67% reduction |
Lessons Learned
-
Start with shared utilities: Date validation and error logging had the clearest boundaries and highest impact.
-
Use copybooks religiously: Every interface change was made in one place and automatically propagated.
-
Dynamic CALL for shared modules: DTEVALID and ERRLOG used dynamic CALL because they were shared across many programs. Module-specific subprograms could have used either.
-
Phased migration works: The system was never "broken" during the transition. Each phase was independently deployable and testable.
-
The driver program should be thin: The final ACCT-MAINT driver was 800 lines of pure orchestration — read, validate, calculate, update, audit. No business logic.
Discussion Questions
-
Why did Maria choose dynamic CALL for all the account-related subprograms? What would the trade-offs have been with static CALL?
-
The interface copybook uses a single 01-level group item rather than separate parameters. What are the advantages and disadvantages of this approach?
-
During Phase 3, how would you approach separating interleaved validation and calculation logic? What strategies would you use to identify the boundaries?
-
If you were adding a new feature — say, a loyalty points calculation — to this system, how would you integrate it without modifying existing modules?
-
What risks does the phased migration approach introduce, and how would you mitigate them?