Parity testing a Mirth Connect migration with replayed messages

Whether you are upgrading to a newer commercial Mirth Connect release or moving channels to an open fork, the dangerous moment is the same: a channel export imports without errors, deploys, and looks fine. The question is whether it does exactly what the old one did with the messages your partners actually send.

Parity testing answers that question with evidence. The idea is simple: run the same messages through the old and new engines and compare everything that comes out.

What "the same" means

For each channel, decide what you will compare. Usually:

  • Encoded output per destination: the message each destination would send or write after its transformer.
  • Acknowledgements: the response the source returns to the sender, including ACK codes and error text.
  • Filtering decisions: which messages each destination accepted or filtered.
  • Errors: which messages errored, at which step, with what message.
  • Side effects: rows written to databases, files written, and calls to external APIs.

Agree the list before you start. A migration that was "tested" against only the happy path will meet its first real difference in production.

Step 1: capture a representative message set

Production message storage is the best source of realistic traffic, but it is also PHI. Do this inside your environment, under your BAA and access rules, and keep samples where they already live wherever possible.

For each channel, pull raw inbound messages covering:

  • normal volume across a typical day or week;
  • every message type and trigger event the channel handles;
  • the edge cases: repeating segments, empty fields, unusual encodings, very large messages;
  • messages that errored or were filtered in production, because those exercise the branches that are easy to break.

If message storage is set low on a channel (Metadata or Disabled), you may not have raw content to capture. Note it, and capture from the sending system or a temporary channel instead.

Step 2: neutralise the destinations

The replay must not send to real partners. For each channel, point destinations at harmless targets in both the old and new environments:

  • replace TCP and HTTP senders with endpoints that record what they receive;
  • point file writers at a scratch directory;
  • point database writers at a test schema, or wrap them so they record the statement and parameters instead of executing them.

Do this the same way on both sides, so the comparison is fair. Keep the configuration in source control alongside the channels.

Step 3: replay through both engines

Send the captured messages, in order, through the old and new channels. For channels where order and timing matter (ADT streams, for example), replay sequentially and at a controlled rate. Record, per message:

  • the response returned to the sender;
  • each destination's encoded output;
  • the status of each connector (sent, filtered, errored, queued).

Message metadata in the engine is useful here, but do not rely on it alone. Record what the stub endpoints actually received.

Step 4: diff, then classify every difference

Compare outputs message by message. Most differences fall into a few groups:

  • Expected and harmless: timestamps, generated control IDs, ordering of attributes. Normalise these in the diff so they stop hiding real differences.
  • Behaviour changes in the engine: a default that changed between versions, a library that parses dates or encodings differently, a deprecated function your JavaScript relied on.
  • Environment differences: a database lookup returning different data, a file path that does not exist, a certificate that is missing.
  • Real bugs, in either the old or the new channel. Sometimes the migration finds a long-standing bug in production. Decide deliberately whether to preserve it.

Every difference gets a decision: normalise, fix, or accept with a written reason.

Step 5: keep the harness

The capture set, the stub destinations and the diff tooling are worth as much as the migration. Keep them in source control, refresh the captured messages periodically, and run them before every engine upgrade, Java update or significant channel change. That is how the next upgrade stops being a project.

Cutover

Parity testing does not replace a cutover plan. For each interface or group, write down the order of operations, who to notify on the partner side, how you will watch the first hours of live traffic, and the steps to move back to the old engine. Rehearse the rollback at least once.

How we use this

Every upgrade or fork migration we run is parity-tested channel by channel this way, and the harness is handed over with the migrated channels. If you want to run it yourself, the steps above are the whole method.


Chartferry is independent and is not affiliated with, endorsed by, or a reseller of NextGen Healthcare. Mirth Connect is a trademark of NextGen Healthcare, Inc.