What you will learn
REST adapters, Webhooks, SQL, Retries, Idempotency.
Engineering the capability
Enterprise APIs can be slow, inconsistently documented, and unavailable at different times. Isolate each behind an adapter with a narrow contract. Normalize data while retaining source identifiers and versions so errors can be traced back to the owning system.
Polling and webhooks have different failure modes. Webhooks can be duplicated or arrive out of order; polling can miss changes if cursors are incorrect. Record event IDs and source versions, process idempotently, and reconcile periodically when the system supports it.
A timeout on a write does not establish whether the write happened. Reuse operation IDs or query the remote result before retrying. Respect rate limits and bound retries across the entire workflow. Retrying every failure at every layer can multiply traffic during an outage.
Reject stale and duplicate updates
records = {}
seen_events = set()
def apply_event(event):
if event["event_id"] in seen_events:
return "duplicate"
current = records.get(event["ticket_id"])
if current and event["version"] <= current["version"]:
seen_events.add(event["event_id"])
return "stale"
records[event["ticket_id"]] = {
"version": event["version"], "text": event["text"]
}
seen_events.add(event["event_id"])
return "applied"
assert apply_event(dict(event_id="e7", ticket_id="t1", version=7, text="new")) == "applied"
assert apply_event(dict(event_id="e6", ticket_id="t1", version=6, text="old")) == "stale"
assert apply_event(dict(event_id="e7", ticket_id="t1", version=7, text="new")) == "duplicate"
assert records["t1"]["text"] == "new"
This illustrates version comparison using trusted synthetic events. Real handlers need signature/authentication checks, tenant scoping, schema validation, and an atomic durable write of both event receipt and record update. In-memory sets do not survive a restart.
Worked case
Ticket events arrive as version 7, then 6, then 7 again. Applying them blindly rolls state backward and duplicates work. Compare versions, ignore the stale update, and deduplicate the repeated event. If the source lacks reliable versions, use a reconciliation read and document the remaining race conditions.
Put it into practice
Continue with the next lab: build a resilient ticket adapter. Build the artifact, record the failure cases, and explain the tradeoff before moving on.