All work
Dukaan Dost · Engineering case study

The legacy ERP

Busy ran the business and could not be switched off, so nothing was ever migrated — a bridge let both systems run at once.

Role
Acting CTO — architecture, build, rollout
Timeline
2024 – ongoing
Stage
In production · live event stream
Outcome
Zero downtime through the entire transition
The bridge service consuming events from the legacy ERP
The modern stack never speaks to Busy directly
The problem

You cannot ask a business to stop invoicing for a weekend.

Busy ERP was the accounting system the company actually ran on. Stock, balances and master data lived there, and every plan for replacing it began with a cutover weekend.

A cutover has one failure mode and it is total: if Saturday goes wrong, nobody can invoice on Monday, and the rollback is a restore from backup plus a lost weekend of transactions. For a business that dispatches daily, that risk was never worth taking for any amount of architectural tidiness.

Daily
dispatch — no window where downtime is cheap
8+
entity types needing to stay in sync both ways
Total
failure mode on a cutover, with a slow rollback
What shipped

Before, and after

01
Strategy
Both systems live, indefinitely
A cutover weekend
02
Coupling
Fenced behind a bridge service
Direct reads from the legacy schema
03
Sync
Two-way across 8+ entity types
Manual re-entry between systems
04
Transport
RabbitMQ, auto-reconnect, gzip
Scheduled exports
05
Risk
--dry-run before anything writes
Run it and find out
06
Downtime
None
A weekend, best case
The decisions

What got chosen, and what got refused

The direction I killed — migrate and cut over

Every version of the migration plan had the same shape: freeze, move, verify, switch. I killed all of them. The plan is only sound if verification is perfect, and verification is never perfect on data that has been accreting inside an accounting system for years. Refusing to migrate was the decision the whole project turned on.

The cutover migration plan, abandoned
KilledFreeze, move, verify, switch — and no way back by Monday

A bridge, so the fence is explicit

One service consumes Busy's events and speaks to the modern stack. Nothing else in the system knows Busy exists. That boundary means the legacy schema cannot leak into new code, and either side can be redeployed or rolled back without the other noticing.

--dry-run on everything that writes

The sync scripts take --dry-run, --force and environment flags, and the default is the safe one. Every run against production was printed and read before it was executed. Nothing shipped that only worked if it worked.

CLI output from a dry-run sync showing pending changes
Every write previewed before it was a write
Where it got hard

The parts that fought back

The legacy stream is not polite

Gzip-compressed events, connections that drop without notice, and no replay. Auto-reconnect and idempotent handling were not optional extras, they were the baseline for it working at all.

Two-way sync means conflict

Both systems can change the same master record. Deciding which side wins, per entity type, was more of the design work than the transport ever was.

There is no finish line

The honest outcome is that the bridge is permanent infrastructure, not a temporary migration aid. Planning it as temporary would have produced something too fragile to still be running.

What I learned
  1. 01

    The best migration is often no migration. Two systems side by side beats one risky switch.

  2. 02

    If a plan's rollback is restore-from-backup, it is not a plan, it is a hope.

  3. 03

    Fence legacy systems behind one service. The moment two places know about the old schema, you own it forever.

More work

Still scrolling? Let's talk.

hiteshpal.8097@gmail.com