BBS SG Bank Business banking · legacy batch → modern ledger
synthetic demo engine: …

BBS SG Bank · modernisation walkthrough

From a nightly COBOL flat-file sweep to an always-on ledger

The same balances are processed two ways: the legacy batch that re-reads its account file for every single operation, and a modern engine that keeps the ledger indexed in memory. Everything below runs live against this machine.

Ledger total
12 demo accounts
Posted postings
accepted by the engine
Rejected postings
validation caught them
Invariant
in-memory vs accounts.dat

Pre-modernisation

COBOL nightly batch

legacy/bank.cob
BBS SG BANK · CICS PROD · NBATCH 22:00

          
Open 1980s operator terminal ↗ Start legacy-ui/run.sh locally for live COBOL. This panel replays the output format.
  • Whole-file scan per operation. bank.cob reopens and reads every account row to validate, then again to rewrite it.
  • Batch window only. Balances move once a night, so the branch sees yesterday's position all day.
  • No audit trail. The program prints three counters and nothing an auditor can query later.
  • Fragile records. Fixed 8/12-character fields; a misaligned byte silently changes a balance.
Show the bottleneck in legacy/bank.cob
Loading source…

Post-modernisation

Modern ledger engine + site API

modern/bank.py
Engine latency last batch, engine only
Postings/sec rows ÷ engine time, last batch
Availability Always on postings API, no batch window

Largest balances

    • Waiting for the ledger…
    • Indexed ledger. Balances are applied in one pass; reads stay constant-time as rows grow.
    • Post any time. The UI and API post through the same engine the batch uses.
    • Auditable by default. Every accepted and rejected row is timestamped with a reason.
    • Typed boundaries. Amounts are integers of cents; ids are normalised before they reach the engine.

    Side by side

    What actually changes

    Comparison of the legacy batch and the modern ledger
    ConcernLegacy batchModern ledger
    Posting windowNightly batch onlyAny time, per request
    Per-operation file I/OWhole-file read(s) and a rewriteSingle pass over an in-memory map
    InterfaceGreen-screen job submissionResponsive web UI + JSON API
    AuditThree counters on a consolePer-row entries with reasons
    Rejection handlingCounted, not explainedCounted and explained per row
    Value conservationVerified by eyeballing totalsChecked automatically every batch
    RecoveryRerun the night job and hopeReplay the audit trail

    This table describes the two code paths in this repository. Performance claims live on the Benchmark tab and are only shown when they were measured on this machine.

    Under the hood

    How this demo is wired

    1. 1

      Browser

      Static HTML, CSS and vanilla JS. No build step, no CDN, no third-party scripts.

    2. 2

      Local Python API

      Standard-library HTTP server on localhost. Holds the demo ledger and an audit trail.

    3. 3

      Shared engines

      Postings are written to accounts.dat/operations.dat and applied by the unmodified modern/bank.py; benchmarks reuse scripts/benchmark.py fixtures.

    Ledger

    Accounts

    Demo customer accounts. Balances reflect the seeded history replayed through the batch engine.

    Demo accounts and balances
    Account Name Product Opening Balance Rows
    Loading accounts…
    Ledger total

    Postings

    Accounts & transfers

    Post a deposit, withdrawal or transfer. Each posting is validated, applied by the engine and written to the audit trail.

    New posting

    deposit
    Operation type

    Recent postings

    • No postings yet.

    Interactive

    Batch console

    Generate a transfer-only batch across the live ledger and run it through the modern engine. Transfers move value between accounts, so the ledger total must come out unchanged.

    Pick a size and run it. The console shows what the engine accepted, what it rejected and why.

    Measured, not claimed

    Benchmark

    Both engines are timed as separate processes on identical fixtures generated by the shared harness. If GnuCOBOL is missing, the legacy side is reported as unavailable — never estimated.

    Fixture size:

    Legacy batch

    legacy/bank.cob
    Not measured yet.

    Modern engine

    modern/bank.py
    Not measured yet.

    Verdict

    Run the measurement to see a comparison for this machine.

      History

      Audit trail

      Every row the engine saw: posted and rejected, with the reason and the resulting balance.

      Audit trail of demo postings
      Entry Time (SGT) Type From To Amount Status Detail
      Loading audit trail…