Keeping a bidirectional integration honest.
A CRM built as the modern face of a trusted engine — writable on both sides, measured in seconds.
The situation
A global professional services organisation runs its client data on a long-established in-house system: decades of refinement, deeply trusted, and increasingly hard on its users as the interface aged. Dynamics 365 was introduced not to replace it but to stand in front of it — a modern face where people read and write, while the established system remains the system of record. What began as a mask has grown into a mirror: work happens on both sides, every change flows to the other, and when the two disagree, the rule is simple — the source system wins.
That makes the synchronisation the platform’s spine, not an add-on. Creates, updates, deletions and consent flow both ways across five European markets — hundreds of inbound changes on a typical day, peaking above a thousand. DynaVerto leads solution architecture and technical delivery on the platform.
What we did
Every inbound change lands twice. It is first persisted and mirrored one-to-one in a staging layer, then transcribed into the CRM model in a second step — so every change is inspectable and replayable, and “what exactly arrived, and when” is never a matter of memory. Outbound, the platform computes deltas against that same mirror, so only genuine differences travel; rapid successive edits are consolidated into a single message carrying the final state. The content of a message, not the count of messages, is the evidence.
Every synchronisation-driven write carries a provenance marker, so downstream logic can tell a sync pass from a user action, and change detection short-circuits echoes before they can loop. References to records that have not arrived yet resolve to temporary stand-ins that the real record later overwrites by key — arrival order stops mattering. Duplicate defence is keyed to each record’s true parent rather than to links applied asynchronously; checking the convenient link instead would quietly create duplicates.
“Real time” is measured, not asserted. Over the most recent five thousand inbound changes in production, the average lag from arrival to applied was 3.6 seconds — three quarters inside five seconds, 98% inside fifteen.
That robustness was bought the hard way. The ghost-record defect absorbed three shipped fixes before the real cause surfaced. Each one answered the same question — how does the process know not to create this record — by tightening the guard around the creation path. The question was wrong. The record was being reconstructed from stale values the parent still carried after its child had been deleted; once the leftover data was cleared at the source, the guard became defence in depth rather than the fix. Since then, no fix ships without a reproduction that fails before it and passes after it, with evidence from the live channel.
One window stays open on purpose. A user edit landing inside the seconds-long asynchronous lag can still resolve against a stale value. Closing it would mean making that write synchronous — deeper transaction nesting, longer locks, and timeout risk on exactly the largest records that already sit closest to the limit. The residual was judged cheaper than the cure, and written down instead of quietly carried.
The operational layer is deliberately unglamorous: alerting wired into the team’s channel; a daily triage log in which every alert ends as one of a small, fixed set of decisions rather than a shrug; a numbered dead-letter procedure; and a nightly twelve-test end-to-end suite with timing baselines that separate a slow environment from a real defect.
In five lines
- A staging mirror between the systems: every change inspectable and replayable, deltas computed against it on the way out
- Provenance markers on every sync-driven write — echo loops stopped at the source
- Consecutive edits consolidated into one outbound message — message content, not message count, is the evidence
- Inbound latency measured in production: 3.6 seconds average, 98% within fifteen
- Dead-letter recovery decided per record, not per queue: each affected record classified as already superseded, stale or lost before anything was re-sent
Results
- Sub-five-second synchronisation for three quarters of all inbound changes, measured over the most recent five thousand in production — with the source system authoritative on every conflict
- A production incident that dead-lettered roughly 1,500 messages — 1,370 distinct records — ended with every one of those records identified, re-synchronised where needed, and individually confirmed to carry a later successful update from the system of record. No lost updates, and the count is the evidence rather than the claim
- Half a dozen interlocking synchronisation defects root-caused and closed, each with pre/post evidence
- The sync chain is guarded nightly by an automated end-to-end suite — regressions surface the next morning, not in production weeks later
All engagements are anonymised. References available on request, subject to client approval.
An integration that “mostly works”?
If your integration “mostly works”, thirty minutes is enough to map where it doesn’t.