Case Study: MedClaim — Wrapping Claims Processing as a Microservice

The Business Problem

MedClaim Health Services had a customer service problem. Provider offices (doctors, hospitals, clinics) needed to check the status of submitted claims. The only way to do this was to call MedClaim's claims department and speak to a representative who would look up the claim on a 3270 terminal.

The volume was staggering: 2,000 status inquiry calls per day, with an average call duration of 4 minutes. At a fully loaded cost of $0.50 per minute for call center staff, status inquiries alone cost MedClaim $2 million per year.

The business wanted a self-service web portal where provider offices could check claim status directly. The technology team was asked to deliver it in three months.

The Technical Challenge

The claim status functionality existed in CLM-STATUS, a CICS transaction written in COBOL. The program:

  1. Accepted a claim number from a 3270 BMS screen
  2. Queried the CLAIMS table in DB2
  3. Retrieved related data (provider info, member info, payment details)
  4. Displayed the results on a formatted 3270 screen

The business logic was sound — it had been running correctly for 12 years. The problem was the interface: it was accessible only through a 3270 terminal.

James Okafor's Approach

James proposed a three-step approach:

Step 1: Separate Logic from Presentation

The existing CLM-STATUS program mixed business logic (DB2 queries, status determination) with presentation logic (BMS map formatting, cursor positioning). James refactored it into two programs:

  • CLM-STATUS-API: Pure business logic, communication via COMMAREA
  • CLM-STATUS-UI: 3270 presentation, calls CLM-STATUS-API via EXEC CICS LINK

This refactoring was the riskiest part of the project because it involved changing working COBOL code. James mitigated the risk by: - Writing unit tests (Chapter 34) for the business logic before refactoring - Running the original and refactored versions in parallel for one week - Comparing outputs for 50,000 inquiry transactions

The parallel run found zero discrepancies.

Step 2: Deploy z/OS Connect

MedClaim already had z/OS Connect licensed (unused). James configured a service definition that mapped the CLM-STATUS-API COMMAREA to a REST endpoint:

Service: ClaimStatusInquiry
Method:  GET
Path:    /api/v1/claims/{claimNumber}/status

Request Mapping:
  Path parameter "claimNumber" → CA-CLAIM-NUMBER (position 1, length 12)
  Constant "IQ" → CA-REQUEST-TYPE (position 13, length 2)

Response Mapping:
  CA-RETURN-CODE → responseCode
  CA-CLAIM-STATUS → status (with value mapping)
  CA-RECEIVED-DATE → receivedDate (YYYY-MM-DD format)
  CA-ADJUD-DATE → adjudicatedDate
  CA-PAID-DATE → paidDate
  CA-BILLED-AMOUNT → billedAmount (packed decimal → JSON number)
  CA-PAID-AMOUNT → paidAmount (packed decimal → JSON number)
  CA-REJECT-REASON → rejectReason (trimmed)

Value mapping for status:

COBOL Value JSON Value
"R" "RECEIVED"
"V" "IN_REVIEW"
"A" "ADJUDICATED"
"P" "PAID"
"X" "REJECTED"
"H" "PENDING"

Step 3: Build the Web Portal

The web team built a React-based portal that called the REST API. From their perspective, they were calling a standard REST service — they didn't need to know that COBOL was involved.

The Integration Architecture

┌──────────────┐     ┌──────────────┐     ┌──────────────┐
│  Provider     │     │  API Gateway │     │  z/OS        │
│  Web Portal   │────►│  (Rate limit,│────►│  Connect     │
│  (React)      │     │   Auth,      │     │              │
│               │◄────│   Logging)   │◄────│  CLM-STATUS  │
└──────────────┘     └──────────────┘     │  -API        │
                                          │  (COBOL)     │
┌──────────────┐                          │              │
│  Claims Rep  │                          │  CLM-STATUS  │
│  3270        │──────────────────────────►│  -UI         │
│  Terminal    │◄──────────────────────────│  (COBOL)     │
└──────────────┘                          └──────────────┘

Both the web portal and the 3270 terminal call
the same business logic (CLM-STATUS-API).

Testing Strategy

James implemented three levels of testing:

Contract Tests

Verified that the API contract (request format, response format, status codes) matched the OpenAPI specification:

def test_claim_found():
    response = requests.get(
        f'{BASE_URL}/api/v1/claims/CLM000012345/status',
        headers={'Authorization': f'Bearer {TOKEN}'}
    )
    assert response.status_code == 200
    data = response.json()
    assert 'status' in data
    assert data['status'] in [
        'RECEIVED', 'IN_REVIEW', 'ADJUDICATED',
        'PAID', 'REJECTED', 'PENDING'
    ]
    assert 'billedAmount' in data
    assert isinstance(data['billedAmount'], (int, float))

def test_claim_not_found():
    response = requests.get(
        f'{BASE_URL}/api/v1/claims/XXXXXXXXXXXXX/status',
        headers={'Authorization': f'Bearer {TOKEN}'}
    )
    assert response.status_code == 404

Data Transformation Tests

Verified that COBOL data types were correctly converted:

def test_packed_decimal_conversion():
    """COMP-3 PIC S9(9)V99 should convert to decimal number"""
    response = requests.get(
        f'{BASE_URL}/api/v1/claims/{KNOWN_CLAIM}/status'
    )
    data = response.json()
    # Known claim has billed amount of $1,234.56
    assert data['billedAmount'] == 1234.56

def test_ebcdic_string_trimming():
    """COBOL PIC X(40) should be trimmed of trailing spaces"""
    response = requests.get(
        f'{BASE_URL}/api/v1/claims/{REJECTED_CLAIM}/status'
    )
    data = response.json()
    assert not data['rejectReason'].endswith(' ')

Load Tests

Verified that the API could handle expected traffic:

Load Test Results:
  Concurrent users: 100
  Requests per second: 500
  Average response time: 180ms
  95th percentile: 340ms
  99th percentile: 720ms
  Error rate: 0.01%
  Duration: 30 minutes
  Total requests: 900,000
  Status: PASSED

Results

Metric Before After (3 months) After (6 months)
Status inquiry calls/day 2,000 1,300 800
Call reduction 35% 60%
Annual call center savings $700K | $1.2M
Provider satisfaction score 3.2/5.0 4.1/5.0 4.4/5.0
Average inquiry response time 4 min (call) 2 sec (web) 2 sec (web)
API availability N/A 99.95% 99.97%
COBOL code changes 1 refactoring (split) 0

The Unexpected Benefits

  1. Data for analytics: Every API call was logged, giving MedClaim data on which providers checked claims most frequently, which claim statuses generated the most inquiries, and peak inquiry times. This data informed staffing decisions and process improvements.

  2. Foundation for more APIs: The infrastructure (z/OS Connect, API Gateway, monitoring) was reusable. MedClaim subsequently exposed claim submission, eligibility verification, and payment remittance as APIs — each taking only 2-3 weeks.

  3. Developer recruitment: The job posting for "COBOL developer working on REST APIs and microservices" attracted candidates who would never have applied for a traditional COBOL role.

Discussion Questions

  1. James chose to refactor CLM-STATUS into two programs (API and UI) rather than wrapping the existing program as-is. What was gained by this refactoring? What would have been lost?

  2. The parallel run of old and new versions for one week was a key risk mitigation. How would you handle a scenario where the parallel run revealed a discrepancy? What would your decision process be?

  3. Provider satisfaction jumped from 3.2 to 4.4. What does this suggest about the relationship between technology modernization and business outcomes?

  4. The project delivered $1.2M in annual savings on a $380K investment. Calculate the ROI and payback period. How would you present this to justify additional modernization projects?