“Tell me before I continue.”
A stock check or an authorization may belong on the immediate path. Keep the dependency set small, carry a deadline, and define what a missing answer means.
Every integration brings another clock, failure mode, and interpretation of “done.” The interesting part is deciding how much of that uncertainty your caller should inherit.
Read the practical guideStock takes 120 ms, delivery 250 ms, and recommendations 180 ms. These are independent reads, so all three can run together.
The read finished successfully after 1 attempt.
Calculated outcome for this setup · the replay changes the inspected moment.
All three independent reads start together. The caller waits 250 ms for the slowest lane, including its retries. All required data is available.
Parallel reads overlap waiting but put more requests in flight at once; this single operation does not predict system throughput. With no retries, a transient error ends that provider lane immediately. Timeouts limit each attempt, not the whole operation, and do not prove remote work stopped. These are read requests; retrying writes requires explicit duplicate-handling semantics. Optional isolation tolerates missing recommendations; this collect-all model still waits for that lane to finish.
These are independent read requests. Every started read settles before the operation’s result is collected, even when recommendations are optional. Queued workers start after a fixed 30 ms acknowledgement, with no existing backlog. The cache is already populated; its age is not simulated.
Retries wait 200 ms, then 400 ms. This repeatable sample omits jitter and Retry-After variation. A production client needs an end-to-end deadline, bounded concurrency, and a retry policy appropriate to the provider. Timed-out remote work may continue, so the caller’s concurrency count is not a provider capacity estimate.
The timing bars are a calculated example, not a throughput benchmark. Drag the clock or replay the request to inspect the sequence.
A stock check or an authorization may belong on the immediate path. Keep the dependency set small, carry a deadline, and define what a missing answer means.
Read independent data together to shorten the wait. Parallel calls still form a synchronous contract when the caller waits for the combined result.
Background work needs an operation ID and a way to discover its outcome. Polling, events, or webhooks close that loop; a 202 response only confirms acceptance.
Async request–replyStart with the symptom. Pick a pattern for that failure mode, then check the new responsibility it creates.
A customer has already seen an error, but the server still holds connections and waits for a remote response.
Budget the complete operation. Set connection and request timeouts, verify whether DNS and TLS are covered, and leave time to respond to the caller. Choose limits from observed downstream latency and the remaining request budget.
Short deadlines release resources sooner but can abandon useful work. A timeout does not prove the remote operation failed.
A struggling provider receives more traffic, or the same order is created twice after an ambiguous timeout.
Identify retryable failures, cap attempts and elapsed time, and use backoff with jitter at a deliberate layer. Retry writes only when the provider’s idempotency contract makes repetition safe; retain the same operation key.
Retries can recover transient failures, but spend latency and downstream capacity. Independent retries across several layers multiply that load.
An unavailable shipping provider exhausts a shared connection pool, and unrelated requests begin timing out.
Set concurrency limits per dependency or workload with separate pools or semaphores. Consider a circuit breaker for repeated failures, and decide what callers receive when their allocation is full or the circuit opens.
Isolation keeps some capacity available for other work, but partitioned resources can sit idle. A breaker limits new calls; it does not restore the provider.
Requests are accepted quickly while customers wait longer for the actual work to finish.
Use durable storage, bound worker concurrency, and monitor queue depth and age. Throttle or reject intake when capacity cannot keep up. Move persistently failing messages to a monitored dead-letter queue with an owner and a replay procedure.
A queue absorbs bursts, but sustained excess demand still accumulates. Acknowledging a queued request confirms acceptance; completion needs its own status.
Repeated reads become cheaper, but users keep seeing a value that changed in the source system.
Choose freshness requirements per kind of data. Set expiration deliberately and invalidate relevant entries after writes. With cache-aside, update the source before invalidating its cached value; decide which operations must read authoritative data.
Longer cache lifetimes reduce origin traffic while increasing staleness. Cache-aside does not guarantee consistency, and cache misses still depend on the source.
An order exists locally while the downstream service never learns about it, leaving the workflow partially completed.
Commit the business change and an outbox record in one local transaction, then publish through a relay. Make consumers tolerate redelivery. When a workflow spans independent commits, define its recovery and compensation steps explicitly.
An outbox closes the local dual-write gap; it does not make the whole workflow atomic. Delivery can lag or repeat, and recovery requires operational ownership.
A renamed field or changed response shape reaches clients that deploy on a different schedule.
Document the contract and check changes against existing consumers. Prefer compatible additions where clients tolerate unknown fields. For breaking changes, provide a versioned migration path and an agreed retirement plan instead of assuming every caller can upgrade together.
Compatibility gives consumers time to move, but maintaining multiple versions increases implementation, testing, and support work.
Business rules start depending on a provider’s payload names, status codes, and interpretation of an order.
Put translation behind an adapter with an explicit domain-facing contract. Map meanings as well as data shapes, and make translation failures inspectable. An in-process component can provide this boundary; a separate service is an additional deployment choice.
The boundary reduces coupling to foreign semantics, but its mappings need maintenance. A separate adapter service adds a network hop and another component to operate.
A replay triggers duplicate work, or an older event overwrites information that was already updated.
Apply the provider’s signature and replay checks before accepting events; Stripe verification requires the raw body. Deduplicate deliveries, tolerate reordering, and fetch current resource state when needed. Persist accepted work promptly and process it asynchronously.
Fast acknowledgement moves responsibility into your system. Receipt tracking, reconciliation, and failed-event recovery remain necessary even when the provider retries delivery.
Local success counters stay green while customers report incomplete work across several integrations.
Define service-level indicators around user operations: successful completion, elapsed time, and age of unfinished work. Carry trace or correlation context across calls and messages. Separate original attempts, retries, and fallback outcomes so a successful response cannot hide a failed business action.
Connected telemetry makes failures easier to locate, but collection and retention cost resources. Keep enough diagnostic context without recording sensitive payloads.