How We Approach Data Integration Replay Tooling at 137Foundry
A perspective post, written from the inside of how we actually build these systems for clients, on what makes the difference between a replay tool we feel good about shipping and a "run this script and hope" approach that the client will inherit and fear.
The short version: we treat replay capability as a first-class feature of any integration we build, not a recovery afterthought. The reasoning has been the same across enough client engagements that we now bake it into the integration template from day one.
Photo by Sergei Starostin on Pexels
The pattern we keep landing on
Across roughly twenty data integration projects in the last few years, the replay design we trust is always the same shape:
A durable per-event log on our side of the integration, indexed by upstream event ID, stamped with ingest time and processing outcome.
A check-then-act block in the processing code that looks up the existing downstream record by event ID before deciding to create or update.
A separate writer layer that the processing code calls into for all side-effecting work, so the same code path can run in dry-run mode or live mode.
A replay command that takes mandatory time-window arguments, defaults to a conservative rate limit, and supports a dry-run mode out of the box.
A dashboard view that shows the event log's recent ingest counts, processing outcomes, and any backlog, so on-call can see at a glance whether the integration is healthy.
The reason we keep landing on this shape is that every client engagement we have done has eventually faced the same situation: an upstream outage, a few hours of missed events, and a need to recover without producing duplicates. The teams that had this pattern in place recovered cleanly. The teams that did not had a follow-up incident that was usually worse than the original outage. The pattern is not novel; it is just the answer to the question, and we have stopped pretending each client's situation requires a fresh design.
What we have learned to push back on
A few specific client requests come up often, and we have learned to push back on them politely but firmly.
"Can we skip the per-event log and just rely on the upstream system's buffer?" We almost always say no. The upstream system's retention is theirs; they will change it without telling us; the first time we find out is during an outage. The per-event log is ours and we control its retention. The cost is modest. The value during recovery is the entire point.
"Can we make the replay default to running everything since the outage started?" Also no. The right replay scope is "the specific events that did not succeed," not "everything in the time window." The two are different sets, and replaying the second produces duplicates from events that did succeed during partial-availability periods. We bake the right scoping into the replay tool's interface so the wrong scoping is not an easy mistake.
"Can we skip the dry-run mode for now and add it later?" This is the one we feel most strongly about. A replay tool without a dry-run mode is operationally dangerous, and the version of "later" where the team adds the dry-run mode is almost always "after the first incident where we wished we had it." The marginal cost of building the writer layer with both modes from the start is small. The cost of retrofitting it later is large.
"Replay tooling is one of those features that looks like overkill until the first time you need it, and then it looks like the cheapest thing you ever built. The pattern is well understood; the discipline is in actually building it before you need it instead of after." - Dennis Traina, founder of 137Foundry
The retrofitting cost is what motivates us to push for the full pattern from day one, even on integrations that are "simple enough not to need it." The integrations that turned out not to need a replay tool cost us a small amount of work to build one. The integrations that turned out to need it would have cost the client a multi-day incident if we had not.
Photo by Atlantic Ambience on Pexels
What "good" looks like from the operator's seat
We measure the replay capability against a specific scenario: an on-call engineer, alone, at 2 AM, after a multi-hour upstream outage. Can they:
See on the dashboard that the integration was degraded between time A and time B.
Query the event log for the specific events in that window that did not succeed.
Run a dry-run replay scoped to those events and read the expected outcome counts.
Run a live replay scoped to the same events with the default rate limit.
Confirm via the dashboard that the gap closed.
If any of those steps requires a Slack thread with a senior engineer, ad-hoc SQL against the production database, or hand-editing a script during the incident, the capability is not done. We test against this scenario explicitly before we consider a replay tool shipped. The "drill" is a real exercise we run in staging, with the on-call engineer who will actually be carrying the pager.
The drill is uncomfortable the first time. The second time it is just a habit. By the third one, the engineer has the muscle memory to do it cold at 2 AM, and that is the entire point.
The architectural decisions that compound
A few specific design choices pay off across every integration we have built. They are not novel either; we are not the first people to land on them. But they compound across the lifetime of the integration in ways that are easy to underweight at design time.
The first is the writer layer. Pulling all side-effecting calls behind a writer interface costs maybe a day of design work at the start of the project. Across the lifetime of the integration, it makes dry-run mode possible, makes testing radically easier, makes the next migration to a different downstream system tractable, and prevents an entire class of "we forgot to honor the dry-run flag in this one path" bugs.
The second is the per-event log. Adding a Postgres table to the integration from day one costs an hour. Across the lifetime, it makes replay scoping correct, makes the "where did this event go" debugging question answerable in five minutes instead of an afternoon, and makes the integration legible to operations engineers who did not write it.
The third is the rate-limited replay default. Setting a conservative default rate on the replay tool costs zero effort. Across the lifetime, it prevents the "the replay finished, but now the downstream system fell over from the burst" follow-up incident exactly once for each integration that runs into it.
None of these are large decisions individually. Together they are the difference between an integration that the team feels comfortable operating and one that the team is afraid of. The discipline of making all three decisions on day one, not after the first incident, is what we have ended up codifying.
For the full architectural walkthrough of how we build the replay tooling itself, the 137Foundry article on data integration replay covers the event log schema, the idempotency key design, the dry-run mode, and the operational controls. The 137Foundry data integration service page has the surrounding context for how we approach integration projects from initial design through production. For the broader set of reliability patterns we lean on across client work, the 137Foundry articles cover related topics like schema drift detection, integration error queues, and idempotency for retry-prone webhooks. The underlying technical concepts are well-grounded in widely cited references like the Wikipedia article on idempotence and the Apache Kafka documentation, which most teams already know.













