Skip to content
Back to Blog
migration architecture patterns

The Strangler Fig: How We Migrated Critical Financial Integrations Without Downtime

Moving a live payment sync from Python to TypeScript while keeping the books balanced to the penny.

QuickBridge Engineering |

In software engineering, rewriting a system from scratch is usually a mistake. When that system handles financial data—like syncing thousands of hotel invoices to QuickBooks—a rewrite isn’t just a mistake; it’s a liability.

When we decided to upgrade QuickBridge from our split-brain v2 architecture (Django + Node.js) to our unified v3 engine (Node.js only), we couldn’t just “turn off” the old system. We had hotels running live operations that needed their data synced every 15 minutes.

Instead of a “Big Bang” rewrite, we used a pattern known as the Strangler Fig.

What is the Strangler Fig Pattern?

Named after a type of fig tree that grows around a host tree until it eventually replaces it, this pattern involves building the new system around the edges of the old one, gradually taking over functionality until the old system can be safely decommissioned.

For our critical eZee to QuickBooks Desktop integration, here is how we applied it.

Step 1: The Hybrid Phase

Our v2 architecture was split:

  • The Brain (Django): Decided what to sync and how to map the data (e.g., “This hotel stay is an Invoice”).
  • The Muscle (Node.js): Actually connected to the APIs to fetch and push the data.

This meant every sync required constant chatter between the two.

In Phase 1 of our migration, we didn’t kill the Brain. We simply taught the Muscle to think for itself. We ported the complex mapping logic—the rules that decide if a transaction is a SalesReceipt or an Invoice—from Python to TypeScript.

The “Float” Trap

This wasn’t just copy-pasting code. Python and JavaScript handle numbers differently.

  • In Python, Decimal('10.00') is precise.
  • In JavaScript, 10.00 is a floating-point number that can sometimes act like 9.99999999.

When dealing with financial data, “close enough” is a bug. We had to implement strict currency libraries in our new TypeScript layer to ensure that a $100.00 invoice in the old system didn’t become $99.99 in the new one.

Step 2: The “Zombie” Runs

Once the logic was ported, we started running the new workflows in parallel with the old ones, but in “shadow mode.” The new system would fetch the data and map it, but not deliver it.

We compared the results. If the old Python code produced an invoice for $500 and the new TypeScript code produced $500, we knew we were safe.

During this phase, our Django backend essentially became a “Zombie”—it thought it was running the show, triggering the workflows, but the Node.js Tunnel was actually doing all the heavy lifting.

Step 3: The Cutover

Once we had verified thousands of syncs, the cutover was boring—which is exactly what you want.

We simply updated our configuration to tell the system: “Stop asking Django for instructions. Read your config directly from the database.”

Because the “Muscle” (Node.js) had already learned how to do the “Brain’s” work during the Hybrid phase, the switch was seamless. No downtime, no lost invoices, and no 3 AM pager alerts.

Why This Matters

Migrations are often sold as “tech debt cleanup,” but for us, it was a customer experience upgrade. By moving to this unified v3 architecture, we cut the latency of these syncs by removing the network hop between Python and Node.js.

The Strangler Fig pattern allowed us to get there without putting our customers’ data at risk.