Salesforce integration patterns: a practical guide to what survives production
Most Salesforce integrations work on demo day and fail eighteen months later, usually at 2 a.m. Here's how the platform's own integration patterns map to production reality — and the four failure modes behind the expensive incidents.
A sales rep saves an opportunity and waits. Behind that save, an Apex trigger makes a synchronous callout to the ERP to reserve stock, and the whole transaction holds its breath until the ERP answers. For two years this works. Then the ERP team schedules a database migration, response times drift from 800 milliseconds to 40 seconds, and every opportunity save in the org starts failing with a callout timeout. Nobody changed a line of Salesforce code.
That incident isn’t exotic. It’s the predictable result of choosing an integration mechanism (a callout) without choosing an integration pattern (a deliberate answer to “what happens when the other system is slow, down, or lying?”). Salesforce has published its own integration patterns catalogue for years, distilled from its architects’ project work, and in our experience the orgs that hurt the most are the ones where nobody has read it.
This guide walks through the five patterns that carry most production traffic, the two questions that choose between them, where event-driven architecture with Platform Events and Change Data Capture fits, and the four failure modes — timeouts, non-idempotent retries, unbatched syncs, and row locks — that turn integrations into incident reports.
The five Salesforce integration patterns that carry production traffic
Salesforce’s Integration Patterns and Practices guide catalogues six named patterns. Five of them account for nearly all the integration traffic we see in real orgs; the sixth is a useful escape hatch. The names are worth learning verbatim, because they give your team a shared vocabulary for design reviews.
Remote Process Invocation — Request and Reply. Salesforce calls a remote system and waits for the answer before continuing, typically via an Apex HTTP or SOAP callout or an External Service. Use it when the user genuinely cannot proceed without the response — a credit check before quote approval, an address validation before save. Because the transaction blocks, Salesforce’s guide reserves this pattern for small-volume, real-time work and explicitly warns against using it for batch payloads.
Remote Process Invocation — Fire and Forget. Salesforce hands a message to the remote system, gets an acknowledgment of receipt, and moves on. The remote process finishes on its own time. Platform Events are the modern mechanism here; outbound messaging is the legacy one, and it still has a redeeming feature — Salesforce retries undelivered outbound messages for up to 24 hours, at intervals that grow from 15 seconds to 60 minutes. With Platform Events there’s no Salesforce-side retry: events sit on the bus and it’s the subscriber’s job to keep up or replay.
Batch Data Synchronization. Data is replicated between Salesforce and an external system on a schedule, in bulk, usually through an ETL tool and the Bulk API. This is the right pattern when timeliness doesn’t matter — nightly ERP customer masters, warehouse loads, data migrations. It’s also the pattern teams skip because “real-time is better,” which is how a nightly job becomes 40,000 individual API calls at 2 p.m.
Remote Call-In. The external system initiates: it authenticates to Salesforce and creates, reads, updates, or deletes records through the REST, SOAP, Bulk, or Apex-exposed APIs. Every ESB, iPaaS connector, and custom script writing into your org is running this pattern. Salesforce’s guidance is blunt about volume: any operation touching more than 2,000 records is a good candidate for Bulk API 2.0 rather than row-by-row REST calls.
UI Update Based on Data Changes. The Salesforce UI refreshes automatically when data changes — a service console that updates the moment a payment lands, without the agent hammering refresh. It’s implemented by subscribing the UI to Platform Events or Change Data Capture streams via the streaming/Pub-Sub channels. Small pattern, big usability payoff, and it keeps users from re-triggering work because the screen looked stale.
The sixth pattern, Data Virtualization, is the “don’t copy the data at all” option: Salesforce Connect surfaces external data as external objects, queried live. When the question is “do we really need this data in Salesforce?”, the honest answer is often no — and virtualization removes an entire class of sync failures by removing the sync.
| Pattern | Timing | Who initiates | Typical mechanism | Classic failure mode |
|---|---|---|---|---|
| Request and Reply | Synchronous | Salesforce | Apex callout, External Services | Callout timeout blocks users |
| Fire and Forget | Asynchronous | Salesforce | Platform Events, outbound messaging | Lost events, no subscriber replay |
| Batch Data Synchronization | Asynchronous | Either (scheduled) | ETL + Bulk API | Row locks, partial-failure blindness |
| Remote Call-In | Synchronous call, async jobs | External system | REST/SOAP/Bulk API 2.0 | Duplicates from blind retries |
| UI Update on Data Changes | Asynchronous | Salesforce | Platform Events / CDC + streaming | Event allocation exhaustion |
| Data Virtualization | Synchronous | Salesforce | Salesforce Connect | External system latency in the UI |
Choosing a pattern: two questions do most of the work
Salesforce’s own selection matrix reduces to two dimensions, and they resolve most design debates before anyone opens a whiteboard tool.
- Timing: does anything genuinely block on the answer? Synchronous means blocking — a user or transaction waits. Asynchronous means the caller continues and results arrive later. Be ruthless here: “the business wants it real-time” usually means “the business wants it within a minute,” which is comfortably asynchronous. Every integration you move out of the synchronous path is one that can no longer time out in a user’s face.
- Direction: who initiates? Salesforce calling out points you at Request and Reply or Fire and Forget. An external system calling in points you at Remote Call-In. Scheduled movement of large data sets in either direction is Batch Data Synchronization.
There’s an implicit third question — volume — and it mostly acts as a veto. Synchronous patterns are for small payloads; the guide is explicit that Request and Reply exists for small-volume, real-time activity. If the payload is thousands of records, you’re in batch territory whatever the stakeholders’ Gantt chart says.
One habit worth stealing from the matrix: name the pattern in the design doc. “We’re doing Fire and Forget via Platform Events, so the subscriber owns retries and error handling” is a sentence that surfaces the hard questions early. “We’ll integrate via API” is a sentence that defers them to production.
Event-driven integration with Platform Events and Change Data Capture
Event-driven architecture is the newest layer in Salesforce’s catalogue — the guide’s own appendix frames the event bus as the decoupling mechanism that lets publishers and subscribers scale independently. Two features implement it, and they’re not interchangeable.
Platform Events are messages you define and publish deliberately — Order_Shipped__e, with exactly the fields subscribers need. Change Data Capture publishes automatically whenever records change on objects you’ve selected: create, update, delete, undelete, with the changed fields in the payload. The rule of thumb we use matches Salesforce’s design guidance: CDC for “mirror my data changes outward” replication, Platform Events for “announce a business fact” processes where you control what gets published and when.
Both ride the same event bus, and the bus has physics you must design for:
- Retention is finite. Platform event and change event messages are stored for 72 hours. A subscriber that’s down over a long weekend can replay what it missed; one that’s down for four days has a data gap you’ll be reconciling by hand.
- Replay is the subscriber’s job. Each event carries a replay ID marking its position in the stream. Durable subscribers persist the last replay ID they processed and resume from it after a crash — and because replay IDs aren’t guaranteed to be contiguous, they’re checkpoints, not sequence numbers to do arithmetic on.
- Duplicates happen. Between replays, reconnects, and retries, the same event can be delivered more than once. Consumers must be idempotent — more on that below.
- Allocations are real. Event publishing and delivery are metered. The trap Salesforce’s architects specifically warn about: CDC publishes an event for every change, so a batch job or data load that touches a million rows can burn through your daily event allocation and cause failed deliveries for everything else. Filter at the source — channel filters, record-triggered conditions, or publishing from Apex only when a meaningful field changed.
Event-driven is a genuine upgrade over polling for most “tell the other system something happened” cases. It is not a way to avoid thinking about failure — it just relocates the thinking from the publisher to the subscriber.
The four failure modes behind expensive integration incidents
Patterns fail in production for depressingly repetitive reasons. These four account for most of the integration incidents we’re brought in to unpick, and every one of them is designable-out in advance.
1. Callout timeouts in the synchronous path
Apex callouts default to a 10-second timeout, configurable up to 120 seconds, with a cumulative cap of 120 seconds of callout time per transaction and a hard limit of 100 callouts. Those numbers are generous, and that’s the trap — a 120-second timeout in a user-facing transaction means a user staring at a spinner for two minutes before the failure they were always going to get. Salesforce’s own pattern guidance suggests the opposite instinct: set the callout timeout low (around five seconds) and treat a slow dependency as a failed dependency. Here’s the shape of a callout that fails fast and retries safely:
HttpRequest req = new HttpRequest();
req.setEndpoint('callout:ERP_Service/orders'); // Named Credential
req.setMethod('POST');
// Same key on every retry, so the ERP can deduplicate
req.setHeader('Idempotency-Key', order.Id + '-' + order.Version__c);
req.setTimeout(5000); // fail fast; the default 10s is a choice
HttpResponse res = new Http().send(req);
For genuinely long-running remote work invoked from the UI, use the Continuation framework so the wait doesn’t occupy the request path — or better, question whether the interaction should be synchronous at all.
2. Retries without idempotency
Every integration eventually retries — a middleware redelivery, a user double-click, a timeout where the request actually succeeded but the response got lost. If the receiving side isn’t idempotent, each retry is a fresh mutation: duplicate orders, double-counted payments, triggers firing twice. Salesforce’s pattern documentation is unambiguous that it’s almost impossible to guarantee a message is sent exactly once, so receivers must tolerate repeats.
The mechanics are old and boring, which is a compliment. Inbound to Salesforce: upsert on an external ID field instead of insert, so a replayed message updates rather than duplicates. Outbound from Salesforce: send a stable unique key with every request and let the remote system deduplicate on it — the approach Salesforce’s engineering blog has recommended for over a decade, enforced with a unique database constraint rather than application-side checking. The design review question is one sentence: what happens if this exact message arrives twice? If the answer isn’t “nothing,” the integration isn’t done.
3. The unbatched sync
A sync built record-by-record works fine at 50 records a night. Then the business acquires a competitor, the nightly delta becomes 400,000 rows, and the job that used to finish in minutes is still running at 9 a.m., colliding with users and burning API calls. Bulk API 2.0 exists precisely for this — it handles up to 150 million records per rolling 24-hour period as an asynchronous job, with Salesforce managing the batching.
The subtler half of this failure mode is partial success. Bulk jobs routinely succeed for 199,400 records and fail for 600, and the API tells you exactly which — but only if your integration reads the results. Pipelines that check “did the job complete?” instead of “did every record succeed?” accumulate silent inconsistency for months. Process the failed-record results, retry only those, and schedule the whole thing inside an off-peak batch window, which Salesforce’s batch pattern guidance strongly recommends to avoid contending with users.
4. Row locks from parallel writes
UNABLE_TO_LOCK_ROW is the signature error of a bulk load that ignored the data model. When Salesforce writes a child record, it can lock the parent — to maintain rollup summaries and relationship integrity — and a transaction that waits more than about 10 seconds for a lock fails. Bulk API processes batches in parallel by default, so two batches containing contacts of the same account will fight over the account lock. Add data skew — one account with tens of thousands of children — and every batch queues behind the same lock.
The fixes are well established: sort records by parent ID so children of one parent land in the same batch (Salesforce’s batch pattern makes the same grouping recommendation), reduce batch sizes, and fall back to serial mode when contention persists — accepting that serial trades throughput for reliability. Retry with backoff handles the residue. What doesn’t work is ignoring it: row-lock errors under parallel load are deterministic physics, not bad luck.
Middleware vs. point-to-point: when the extra layer earns its keep
Every one of the patterns above can be built point-to-point — Salesforce talking directly to the other system — or through middleware (an ESB or iPaaS such as MuleSoft, Boomi, or similar). The trade-off is unfashionable to discuss because middleware sounds like enterprise ceremony, but the pattern guide’s own middleware terminology section explains what you’re actually buying: queuing and buffering, protocol translation, transformation, mediation and routing, and orchestration. Salesforce deliberately provides little of this natively — the guide notes that true message queuing for integration scenarios requires a middleware solution, and recommends against coding data transformation in Apex for maintenance and performance reasons.
Point-to-point is the right call more often than vendors admit: two systems, simple mapping, clear data ownership, one direction. A Named Credential, a callout, an external ID, done. The failure curve is in the counting. Five integrations point-to-point is a manageable set; fifteen is a mesh where every retry policy, credential rotation, and field mapping is implemented slightly differently in a slightly different place, and nobody can answer “what happens to orders if the ERP is down for an hour?” without archaeology.
Our rule of thumb: the moment more than two systems care about the same business event, or you find yourself hand-building queuing and retry logic inside Apex, the middleware conversation is overdue. Integration sprawl compounds quietly, the same way org technical debt does — each individual shortcut is defensible, and the sum is an architecture nobody chose. A big part of our integration work is exactly this consolidation: fewer, better-instrumented paths instead of a dozen bespoke ones.
Designing for the day the other system is down
Here’s the shift that separates integrations that survive production from those that generate incident reports: stop designing for the day everything works. That day needs no design. Request and Reply, Fire and Forget, batch sync, Remote Call-In, event-driven — every pattern in the catalogue is really a different answer to the same question: when the remote system is slow, down, or duplicating messages, what happens to my data and my users?
Answer it per integration, in writing, before go-live. What’s the timeout, and what does the user see when it trips? Where do retries live, and what makes them safe to repeat? What’s the replay story when a subscriber has been dark for a day? Who gets paged when the nightly job partially fails — and will they even know, or does the dashboard just show “job completed”?
None of this demands heroic engineering. Almost every mechanism involved — upsert with external IDs, replay IDs, bulk result files, parent-sorted batches, five-second timeouts — is built into the platform and documented in Salesforce’s own patterns guide. What it demands is the discipline to pick a named pattern, state its failure behaviour out loud, and test that behaviour before production does it for you. Teams that build this habit ship integrations that degrade politely and recover on their own. Teams that don’t get to learn each pattern the expensive way, one 2 a.m. page at a time.
Understanding the basics
What are Salesforce integration patterns?
Salesforce integration patterns are the officially documented, reusable designs for connecting Salesforce with external systems, published in Salesforce’s Integration Patterns and Practices guide. The catalogue covers six patterns — Remote Process Invocation (Request and Reply, and Fire and Forget), Batch Data Synchronization, Remote Call-In, UI Update Based on Data Changes, and Data Virtualization — each defined by who initiates the interaction, whether it blocks, and how errors and recovery are handled.
What is the difference between Platform Events and Change Data Capture?
Platform Events are custom messages you define and publish deliberately to announce a business fact, with exactly the fields subscribers need. Change Data Capture automatically publishes an event whenever records change on objects you enable, mirroring creates, updates, deletes, and undeletes. Use CDC for data replication, Platform Events for business processes. Both share the event bus, 72-hour retention, and replay-ID-based recovery, and both require idempotent subscribers.
When should you use middleware instead of point-to-point Salesforce integration?
Point-to-point suits simple cases: two systems, one direction, clear data ownership, light transformation. Middleware earns its cost once several systems consume the same events, or you need queuing, guaranteed delivery, retries, transformation, or centralised monitoring — capabilities Salesforce largely expects an external layer to provide. A practical trigger: if you’re hand-building queues or retry logic in Apex, or maintaining many bespoke connections, consolidate onto middleware.
Wrestling a Salesforce integration that keeps paging your team? Talk to us — making integrations boring again is what we do.
Keep reading
All insights
Salesforce Data 360 vs. Microsoft Fabric: OneLake, the zero-copy bridge, and the job each one is actually for
Salesforce Data 360 vs. Databricks: the zero-copy bridge, the shared models, and the day the partner became a competitor
The Data 360 Query API: running SQL against your unified data from outside Salesforce