Web Applications

Statement rendering migration off a retiring AS/400

Built by Leandro Magbanua

Moved monthly account statements off an RPG job on an AS/400 the vendor had stopped supporting — with a parallel run that took three attempts to pass.

  • Builder email verified

    Verification

Type
Professional
Category
Web Applications
Published
Jul 26, 2026
Updated
Jul 26, 2026
Built withJavaSpring BootPostgreSQLRabbitMQ
TopicsFintech

About this project

The problem

Monthly statements for about 1.1 million accounts were produced by an RPG job on an AS/400 whose vendor had announced end of support, and the one person who could modify the job was approaching retirement faster than the machine was. The bank didn't want a redesign — legal and operations wanted the statements customers received to stay effectively identical, because every layout change generates a wave of branch inquiries. So the project was the least glamorous kind of migration: reproduce a system nobody fully understood, byte for byte where it mattered.

My role

I was a mid-level backend developer then, one of six on the project for about ten months. The retiring RPG developer was assigned to us half-time as a reference, which in practice meant I spent many mornings reading green-screen source with him and taking notes he'd correct the next day. A project manager ran the schedule against the vendor's support cutoff date.

What I owned

I owned the new rendering service — the Java side that took extracted account data and produced the statement output — and, more importantly as it turned out, the parity checker: a diff tool that compared old and new statement runs field by field and produced a report operations could review. The queue-based fan-out that let a month's statements render in parallel across workers was also mine.

Technical & product decisions

The decision that shaped everything was defining parity as content parity, not pixel parity — same figures, same line items, same ordering, but rendered by our own layout code — after a costly week proving that chasing the AS/400's exact spacing was madness. I pushed for building the parity checker before finishing the renderer, which felt backwards to the PM but meant every rendering bug was found by our tool instead of by operations. Fanning render jobs out through RabbitMQ rather than one sequential batch was the difference between a nine-hour run and ninety minutes.

Result & impact

The parallel run failed twice — once on interest-line rounding for a legacy account type, once on a cheque-account ordering rule that existed only in the RPG source — and passed cleanly on the third attempt, which is the fact I quote when people ask why parallel runs matter. We cut over with the AS/400 job kept warm as a fallback for two more cycles and never needed it. The machine was decommissioned on schedule, and statement production went from a system one man could maintain to one the team on-call rotation could actually support.

Who else worked on it

The retiring RPG developer was the project's real source of truth and was generous with knowledge he could have hoarded. A data engineer owned extraction from the core system, two developers built the operations screens and archive integration, and the operations statement team reviewed every parity report — their sign-off, not ours, was the definition of done.