Field notes / Integrations

It worked with one API.
Then you added five.

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 guide

One request, three integrations.

Product availability / independent reads

Stock takes 120 ms, delivery 250 ms, and recommendations 180 ms. These are independent reads, so all three can run together.

Request timeline250 ms
0 ms300 ms600 ms
CallerResult returned
1Stock, attempt 1: success, from 0 to 120 milliseconds.
1Delivery, attempt 1: success, from 0 to 250 milliseconds.
1Recommendations, attempt 1: success, from 0 to 180 milliseconds.
Completed readError / timeoutCached read
Delivery at 250 ms

The read finished successfully after 1 attempt.

Calculated outcome for this setup · the replay changes the inspected moment.

Caller response250 msAll reads have settled
Result settles250 msAll reads succeeded
Network attempts3Includes retries; excludes cache
Peak concurrent calls3Outstanding at this caller
Reading the result

Parallel calls still have a final wait.

All three independent reads start together. The caller waits 250 ms for the slowest lane, including its retries. All required data is available.

Where this setup can still hurt

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.

What this example assumes

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.

Choose the promise before the mechanism

What does the caller actually need?

01 / Synchronous result

“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.

02 / Parallel reads

“These answers are independent.”

Read independent data together to shorten the wait. Parallel calls still form a synchronous contract when the caller waits for the combined result.

03 / Asynchronous completion

“Accept it; tell me what happened.”

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–reply
The practical guide

Problems you meet
between the boxes.

Start with the symptom. Pick a pattern for that failure mode, then check the new responsibility it creates.

01

The caller gives up before its dependency does

A customer has already seen an error, but the server still holds connections and waits for a remote response.

A useful starting point

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.

What you still own

Short deadlines release resources sooner but can abandon useful work. A timeout does not prove the remote operation failed.

02

Retries multiply the original problem

A struggling provider receives more traffic, or the same order is created twice after an ambiguous timeout.

A useful starting point

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.

What you still own

Retries can recover transient failures, but spend latency and downstream capacity. Independent retries across several layers multiply that load.

03

One dependency consumes everyone’s workers

An unavailable shipping provider exhausts a shared connection pool, and unrelated requests begin timing out.

A useful starting point

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.

What you still own

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.

04

The queue grows faster than it can drain

Requests are accepted quickly while customers wait longer for the actual work to finish.

A useful starting point

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.

What you still own

A queue absorbs bursts, but sustained excess demand still accumulates. Acknowledging a queued request confirms acceptance; completion needs its own status.

05

A fast response contains yesterday’s answer

Repeated reads become cheaper, but users keep seeing a value that changed in the source system.

A useful starting point

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.

What you still own

Longer cache lifetimes reduce origin traffic while increasing staleness. Cache-aside does not guarantee consistency, and cache misses still depend on the source.

06

The database commits, but the event disappears

An order exists locally while the downstream service never learns about it, leaving the workflow partially completed.

A useful starting point

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.

What you still own

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.

07

A provider release breaks a working consumer

A renamed field or changed response shape reaches clients that deploy on a different schedule.

A useful starting point

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.

What you still own

Compatibility gives consumers time to move, but maintaining multiple versions increases implementation, testing, and support work.

08

Vendor assumptions spread through the domain

Business rules start depending on a provider’s payload names, status codes, and interpretation of an order.

A useful starting point

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.

What you still own

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.

09

A webhook arrives twice, late, or out of order

A replay triggers duplicate work, or an older event overwrites information that was already updated.

A useful starting point

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.

What you still own

Fast acknowledgement moves responsibility into your system. Receipt tracking, reconciliation, and failed-event recovery remain necessary even when the provider retries delivery.

10

Every service looks healthy, but the operation fails

Local success counters stay green while customers report incomplete work across several integrations.

A useful starting point

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.

What you still own

Connected telemetry makes failures easier to locate, but collection and retention cost resources. Keep enough diagnostic context without recording sensitive payloads.