Proof of Capability

Legacy COBOL Banking System

A production banking system written in 1999. No surviving source documentation. Seven programs, three shared data structures, 25 years of accumulated changes. We ingested it, mapped it, and produced working translations—in under an hour.

7
Programs
111
Functions
101
Dependencies
<1hr
Total Time

Architecture Map

RippleForge ingested the entire codebase and produced this interactive dependency graph. Every function, every call path, every data structure—mapped automatically.

Interactive RippleForge visualization — pan, zoom, and search. Generated automatically from COBOL source. Prefer a static export? We can provide PDF, SVG, and JSON outputs.

Code Translation

The interest calculation engine—one of seven programs. Original COBOL on the left, modern translations on the right. Same business logic, modern language, maintainable code.

Original COBOL (1999)

       IDENTIFICATION DIVISION.
       PROGRAM-ID. INTCALC.
       AUTHOR. EMBER-NATIONAL-IT.
       DATE-WRITTEN. 1999-01-15.
      *================================================*
      * INTEREST-CALC - Interest Calculation Engine
      * Computes daily accrued interest for savings,
      * money market, and CD accounts. Applies tiered
      * rate schedules and handles compounding.
      *================================================*

       DATA DIVISION.
       FILE SECTION.
       FD RATE-TABLE.
       01 RATE-TABLE-RECORD.
           05 RT-ACCT-TYPE       PIC X(2).
           05 RT-TIER-MIN        PIC S9(11)V99.
           05 RT-TIER-MAX        PIC S9(11)V99.
           05 RT-ANNUAL-RATE     PIC S9(3)V9(6).
           05 RT-COMPOUNDING     PIC X(1).
              88 RT-DAILY        VALUE 'D'.
              88 RT-MONTHLY      VALUE 'M'.
              88 RT-QUARTERLY    VALUE 'Q'.

       PROCEDURE DIVISION.
       CALCULATE-INTEREST.
           PERFORM LOOKUP-RATE-TIER
           IF NOT RATE-FOUND
               MOVE 'E007' TO WS-ERROR-CODE
               EXIT PARAGRAPH
           END-IF
           PERFORM COMPUTE-DAILY-INTEREST
           IF WS-INTEREST-AMT < WS-ZERO-FLOOR
               MOVE ZEROS TO WS-INTEREST-AMT
           END-IF.

       COMPUTE-DAILY-INTEREST.
           EVALUATE TRUE
               WHEN RT-DAILY
                   COMPUTE WS-DAILY-RATE =
                       RT-ANNUAL-RATE / WS-DAYS-IN-YEAR
                   COMPUTE WS-INTEREST-AMT =
                       ACCT-BALANCE * WS-DAILY-RATE
               WHEN RT-MONTHLY
                   COMPUTE WS-DAILY-RATE =
                       RT-ANNUAL-RATE / 12 / 30
                   COMPUTE WS-INTEREST-AMT =
                       ACCT-BALANCE * WS-DAILY-RATE
               WHEN RT-QUARTERLY
                   COMPUTE WS-DAILY-RATE =
                       RT-ANNUAL-RATE / 4 / 90
                   COMPUTE WS-INTEREST-AMT =
                       ACCT-BALANCE * WS-DAILY-RATE
           END-EVALUATE.

       APPLY-INTEREST-CREDIT.
           ADD WS-INTEREST-AMT TO ACCT-BALANCE
           ADD WS-INTEREST-AMT TO ACCT-AVAIL-BALANCE
           ADD WS-INTEREST-AMT TO WS-TOTAL-INTEREST
           REWRITE ACCOUNT-RECORD.

       CALC-CD-EARLY-WITHDRAWAL.
           COMPUTE WS-PENALTY-AMOUNT =
               ACCT-BALANCE * WS-CD-PENALTY-RATE
           SUBTRACT WS-PENALTY-AMOUNT FROM ACCT-BALANCE
           SUBTRACT WS-PENALTY-AMOUNT FROM ACCT-AVAIL-BALANCE
           REWRITE ACCOUNT-RECORD.

Python Translation

"""
Interest Calculation Engine
Originally: INTCALC, COBOL, 1999-01-15
Translated by CausalWave RootCause pipeline
"""
from dataclasses import dataclass
from decimal import Decimal, ROUND_HALF_UP
from enum import Enum
from typing import Optional

class AccountType(Enum):
    SAVINGS = "SV"
    MONEY_MARKET = "MM"
    CD = "CD"

class CompoundingMethod(Enum):
    DAILY = "D"
    MONTHLY = "M"
    QUARTERLY = "Q"

@dataclass
class RateTier:
    account_type: AccountType
    tier_min: Decimal
    tier_max: Decimal
    annual_rate: Decimal
    compounding: CompoundingMethod

class InterestCalculator:
    DAYS_IN_YEAR = Decimal("365")
    CD_PENALTY_RATE = Decimal("0.100000")

    def __init__(self, rate_table: list[RateTier]):
        self.rate_table = rate_table
        self.total_interest = Decimal("0.00")

    def _lookup_rate_tier(self, account) -> Optional[RateTier]:
        for tier in self.rate_table:
            if (tier.account_type == account.account_type
                and tier.tier_min <= account.balance <= tier.tier_max):
                return tier
        return None

    def _compute_daily_interest(self, balance, tier):
        match tier.compounding:
            case CompoundingMethod.DAILY:
                daily_rate = tier.annual_rate / self.DAYS_IN_YEAR
            case CompoundingMethod.MONTHLY:
                daily_rate = tier.annual_rate / 12 / 30
            case CompoundingMethod.QUARTERLY:
                daily_rate = tier.annual_rate / 4 / 90

        return (balance * daily_rate).quantize(
            Decimal("0.01"), rounding=ROUND_HALF_UP)

    def calculate(self, account):
        tier = self._lookup_rate_tier(account)
        if tier is None:
            raise ValueError(f"E007: No rate tier for {account.account_number}")

        interest = self._compute_daily_interest(account.balance, tier)

        # Zero floor (2021 patch)
        if interest < Decimal("0"):
            interest = Decimal("0.00")

        account.balance += interest
        account.available_balance += interest
        self.total_interest += interest
        return interest

    def cd_early_withdrawal(self, account):
        penalty = (account.balance * self.CD_PENALTY_RATE).quantize(
            Decimal("0.01"), rounding=ROUND_HALF_UP)
        account.balance -= penalty
        account.available_balance -= penalty
        return penalty

C# / .NET Translation

// Interest Calculation Engine
// Originally: INTCALC, COBOL, 1999-01-15
// Translated by CausalWave RootCause pipeline

using System;
using System.Collections.Generic;
using System.Linq;

namespace EmberNationalBank.Interest
{
    public enum AccountType { Savings, MoneyMarket, CD }
    public enum CompoundingMethod { Daily, Monthly, Quarterly }

    public record RateTier(
        AccountType AccountType,
        decimal TierMin, decimal TierMax,
        decimal AnnualRate,
        CompoundingMethod Compounding);

    public class InterestCalculator
    {
        private const decimal DaysInYear = 365m;
        private const decimal CdPenaltyRate = 0.100000m;
        private readonly List<RateTier> _rateTable;

        public decimal TotalInterest { get; private set; }

        public InterestCalculator(List<RateTier> rateTable)
            => _rateTable = rateTable;

        private RateTier? LookupRateTier(Account acct)
            => _rateTable.FirstOrDefault(t =>
                t.AccountType == acct.AccountType
                && acct.Balance >= t.TierMin
                && acct.Balance <= t.TierMax);

        private static decimal ComputeDailyInterest(
            decimal balance, RateTier tier)
        {
            decimal dailyRate = tier.Compounding switch
            {
                CompoundingMethod.Daily     => tier.AnnualRate / DaysInYear,
                CompoundingMethod.Monthly   => tier.AnnualRate / 12m / 30m,
                CompoundingMethod.Quarterly => tier.AnnualRate / 4m / 90m,
                _ => throw new InvalidOperationException()
            };
            return Math.Round(balance * dailyRate, 2,
                MidpointRounding.AwayFromZero);
        }

        public decimal Calculate(Account account)
        {
            var tier = LookupRateTier(account)
                ?? throw new InvalidOperationException(
                    $"E007: No rate tier for {account.AccountNumber}");

            var interest = ComputeDailyInterest(account.Balance, tier);

            // Zero floor (2021 patch)
            if (interest < 0m) interest = 0m;

            account.Balance += interest;
            account.AvailableBalance += interest;
            TotalInterest += interest;
            return interest;
        }

        public decimal CdEarlyWithdrawal(Account account)
        {
            var penalty = Math.Round(
                account.Balance * CdPenaltyRate, 2);
            account.Balance -= penalty;
            account.AvailableBalance -= penalty;
            return penalty;
        }
    }
}

What This Demonstrates

This is one example. The same pipeline handles any legacy system.

Translations are validated and scoped based on the target runtime, dependencies, and test harness available. Results vary by system complexity.

Got a system like this?

Tell us what you're dealing with. We'll tell you what we can do about it.

Talk to an Engineer