This conversation happens in every business running more than one operational system. Finance produces a figure, operations produces a different figure, and the meeting spends forty minutes establishing which is wrong before anyone asks the better question: what exactly is each system counting?
In our experience the answer is usually that they are counting different things, correctly.
Cause one: the boundary is defined differently
What is a completed delivery?
- To the tracking platform, it might be: the vehicle entered the customer geofence and remained for more than three minutes.
- To the driver app, it might be: the driver pressed 'Delivered' and captured a signature.
- To the invoicing system, it might be: a proof-of-delivery document was received and matched to an order line.
A vehicle that arrived, waited, and left without unloading because the customer's dock was full satisfies the first definition and neither of the others. That is not a data error. It is three teams answering three different questions with the same word.
The fix is not technical. Agree one definition per metric, write it down, and make each system's report state which definition it uses. Most of these disputes evaporate the moment the definitions are on the same page.
Cause two: timezone and day-boundary handling
Tracking platforms usually store timestamps in UTC because devices report from wherever they are. Accounting systems usually work in local business time, because that is what a trading day means. A delivery completed at 02:30 local time can legitimately land in different days in the two systems.
For a month-end report this affects only the boundary days, which is exactly why it is hard to spot: the discrepancy is small, it moves, and it never reproduces when you go looking for it.
A related trap worth knowing
Locale settings can rewrite numerals as well as formats. An application running in Arabic locale will happily render a date as ٢٠٢٦-٠٨-٢١ — correct for a reader, unparseable for a server expecting an ISO date. If that string reaches an API, the request fails silently and returns nothing rather than raising an error, which surfaces as reports that mysteriously come back empty for some users and not others.
Cause three: a sync failed and nobody was told
This is the one that actually costs money.
Integrations are usually built for the happy path. System A posts to system B, system B accepts, everyone is satisfied. Then system B is down for maintenance for eleven minutes, forty-three records fail to post, and the integration — which has no retry, no queue and no alerting — moves on to the next batch.
Nothing appears broken. Both systems are running. The discrepancy is discovered weeks later during a reconciliation, by which time nobody can reconstruct which records were lost or reproduce the conditions.
What a resilient integration actually needs
- Retries with backoff. Transient failures are the common case, and a retry a few seconds later usually succeeds.
- Idempotency. If a retry does go through twice, the receiving system must recognise it as the same record rather than creating a duplicate — which is how you turn a lost-record problem into a double-billing problem.
- A dead-letter queue. Records that cannot be processed after retries go somewhere visible, not into a log file nobody reads.
- Alerting on the queue, not on the error. Somebody should be told that forty-three records are stuck, at the time they get stuck.
- Reconciliation as a feature. A scheduled job that compares record counts between systems and reports drift is unglamorous and catches everything the other four miss.
None of this is difficult. It is simply the part of integration work that gets cut when a project is scoped as 'connect these two systems' rather than 'keep these two systems in agreement'.
How to run the investigation
When the numbers next disagree, resist the instinct to find the wrong one. Instead:
- Get both systems to output the underlying records, not the totals. Totals hide everything.
- Compare the sets rather than the counts. You want the specific records present in one and absent in the other.
- Look at those records' timestamps first. Clustering at day boundaries points at timezone handling; clustering in a narrow window points at a failed sync.
- If they are scattered with no pattern, you have a definitional difference. Go and read how each system decides the record is complete.
That sequence resolves most of these in an afternoon, and it usually ends with a definition being written down rather than code being changed.
Key takeaways
- Two systems disagreeing is usually two correct answers to two different questions.
- Agree one written definition per metric and make each report state which it uses.
- Timezone differences show up only at day boundaries, which is why they are hard to reproduce.
- Failed syncs are the expensive cause: build retries, idempotency, a dead-letter queue and alerting, or you will find out at reconciliation.
- Investigate by comparing record sets, not totals — totals hide the pattern that identifies the cause.
Technical references
- Stripe API: Idempotent requests
One concrete API design for safely retrying requests; behaviour varies by provider.
Questions or a correction? Contact the 2M team. Examples explain a method; results depend on your operation and configuration.
