was in a maintenance window, we had two choices: fail the transfer or decouple from
the constraint. We chose to decouple. That choice became the foundation of every
integration I have designed since.
The Constraint That Changed Everything
The project was IBM IIB-based SEPA credit transfer processing at Oracle Financial
Software. Payments flowed from initiating banks through our platform to clearing networks.
Simple in principle. Brutal in practice when you realise that the clearing window is fixed,
the initiating bank is impatient, and your middleware sits in the middle of a chain where
any link can stall.
The first design was synchronous end-to-end. Each stage waited for the next to confirm.
It was logical. It was also fragile: one slow database acknowledgement during peak batch
hour could hold a thread pool long enough to trigger a cascade that looked — to everyone
watching dashboards — like a complete platform failure. It was not. It was a 400 ms
latency spike in one downstream call. But nothing was isolated.
What Temporal Coupling Actually Costs
Temporal coupling is the condition where two services share the same failure fate because
one calls the other synchronously. The cost is not just downtime — it is the invisibility of
the problem. A slow service does not announce itself. It silently holds resources. Thread
pools exhaust. Connection pools exhaust. The user sees errors. Engineers trace a call chain
that no longer reveals its origin because the slow service recovered three minutes before
anyone started looking.
In payment systems this is not an inconvenience. Missed clearing windows have financial
penalties. Duplicate submissions due to retry ambiguity are a compliance event. The
cost-of-failure calculation is not theoretical — it is priced into the SLA.
All inter-stage message flows use IBM MQ with persistent delivery and session-level correlation IDs. No stage blocks on the next. Every message is durable before the producer acknowledges. Dead-letter queues are first-class operational instruments, not error bins.
Why IBM MQ, and What That Means in Practice
IBM MQ was the right choice for this environment for reasons that go beyond familiarity.
Persistent messaging with guaranteed delivery is not a configuration option in MQ — it is
the default contract. A message written to a queue is on disk before the put() call returns.
If the queue manager restarts, the message is there. If the consuming application crashes,
the message is there. This is not resilience by design. This is resilience by definition.
Multi-instance High Availability — two queue managers sharing a network filesystem, one
active and one in standby — gave us sub-minute failover without a load balancer or health
probe. The standby simply detects that the active queue manager’s lock is gone and takes
over. Applications reconnect and continue. The message that was mid-flight during the
failover is delivered exactly once on reconnection.
Dead-Letter Queues as Signals, Not Failures
The most important operational shift was reframing the dead-letter queue. In synchronous
systems, an error is an event the caller sees immediately and handles. In asynchronous
systems, the DLQ is where you discover that a message could not be processed — and it
tells you exactly which one, with the full original payload and an error description.
A non-zero DLQ depth means something specific happened to a specific message. A DLQ
depth trending upward means something is systematically wrong with a consumer. These are
different problems requiring different responses. Both are better than a synchronous
failure cascade that leaves you no evidence and no path to replay.
The rule I apply to every integration now: if the producer does not need the answer to continue its own work, the call should be asynchronous. The burden of proof is on synchronous design, not async.
Azure Service Bus in Later Work
On Azure-native platforms at T-Systems and IBM Consulting, Azure Service Bus Premium
replaced IBM MQ as the messaging fabric — same architectural principles, different
operational model. Managed identity authentication replaced connection strings entirely.
Dead-letter queues, message sessions for ordered delivery per entity, and auto-forwarding
for fan-out scenarios map directly to the MQ patterns. The shift from on-premises queue
managers to a managed cloud service removed the HA configuration problem without removing
any of the guarantees.
The patterns do not belong to the product. They belong to the problem.