From batch to real-time: what changes when order fulfilment stops running on a schedule

Written by

in

Most order fulfilment builds on Salesforce start the same way: a scheduled job runs every fifteen minutes, or every hour, and picks up whatever’s ready to move. It works. Nobody questions it for the first year. Then the business grows, volumes climb, and someone in operations starts asking why an order placed at 9:01am doesn’t ship until the 9:15 run picks it up. That question is usually where the batch-to-real-time conversation actually starts, not in an architecture review.

What batch processing quietly costs you

A scheduled batch job isn’t wrong. It’s a reasonable default when volumes are low and nobody’s watching the clock. But it carries a hidden tax that only shows up once you look for it: every record sits in a queue for an average of half the run interval before anything happens to it. Run every fifteen minutes, and you’ve built in a 7.5 minute average delay before a single fulfilment step even starts. Stack three or four sequential batch stages, each with its own schedule, and you can lose the better part of an hour before an order actually leaves the warehouse.

That delay is invisible until a customer calls asking where their order is, or a warehouse team flags that they’re getting work in inconsistent bursts instead of a steady stream. I’ve seen contact-centre teams build entire workarounds around a batch cadence they didn’t even know existed, manually checking and re-triggering processes because “it usually catches up by lunchtime.”

The failure mode nobody plans for

There’s a second cost that matters more than latency: batch jobs fail as a unit, or close to it. If a bulk job hits a governor limit or a bad record halfway through a batch of 200, you’re now debugging which of those 200 actually completed, which is a much worse afternoon than debugging one failed record in isolation. Batch design tends to optimise for throughput and quietly deprioritise traceability. Real-time processing, done properly, forces you to think about one record at a time, which makes failures easier to isolate and easier to explain to the business.

The actual trigger for going real-time

It’s rarely “we want to modernise.” It’s almost always a specific, measurable pain point: a customer-facing SLA that batch timing can’t meet, a warehouse team that needs work distributed evenly rather than in spikes, or an integration partner who expects near-instant confirmation. If you can’t point to one of those, real-time isn’t a priority yet, and rebuilding a working batch process for its own sake is a good way to introduce risk without a return.

Where it does matter, the shift usually isn’t “replace the batch job with a trigger.” It’s a genuine change in what fires the work, what carries state, and who’s accountable when something breaks.

What changes underneath

Moving off a schedule means the fulfilment logic has to become event-driven rather than time-driven. On Salesforce that typically means a combination of a few things working together, not one silver bullet:

  • Platform Events or Change Data Capture firing the moment an order record actually changes state, instead of waiting for the next poll
  • Asynchronous Apex (Queueable or async processing) picking up the work so the triggering transaction stays fast and doesn’t hold a user-facing save hostage
  • Flow orchestrating the business logic where it’s genuinely declarative, with Apex reserved for the parts that need bulkification, callouts, or complex branching Flow handles poorly
  • A retry and dead-letter pattern, because real-time doesn’t mean failure-proof. Something will fail at 2am with nobody watching, and you need a way to catch it and reprocess without a human noticing a gap three days later

That last point trips up a lot of first attempts. Teams replace the batch job with a trigger, get excited that orders now process in seconds, and forget that batch processing had accidental resilience built in: if something failed, the next run would often just pick it up again. Real-time architectures need that resilience designed in deliberately, usually through a monitoring object or a Platform Event replay strategy, because there’s no “next run” to bail you out.

Governor limits don’t disappear, they just move

A common assumption is that moving to per-record processing sidesteps bulk governor limits. It doesn’t. It just shifts the risk from “one batch might hit a limit” to “a burst of 500 near-simultaneous order updates might trigger 500 near-simultaneous transactions, each doing a callout.” If your real-time trigger fires a synchronous callout per record with no queuing or throttling, you’ve traded a slow, predictable batch problem for a fast, unpredictable concurrency problem. Bulkification discipline still applies. It just applies to unpredictable, uneven bursts instead of a scheduled, sizeable chunk.

Where teams get this wrong

The most common mistake isn’t technical, it’s scope. Teams try to make everything real-time in one release because the business asked for “faster fulfilment” without qualifying which step actually matters. Not every stage of a fulfilment process needs sub-second latency. Order confirmation to the customer might genuinely need to be instant. Inventory reconciliation with a warehouse system might be perfectly fine running every five minutes. Treating the whole pipeline as one monolithic thing that must become real-time, rather than identifying the two or three steps where latency actually causes pain, is how a two-week enhancement becomes a three-month rebuild.

The second mistake is skipping observability. A batch job that fails is loud, usually because someone notices the batch didn’t run or the record count looks wrong. A real-time process that silently drops one event out of ten thousand is quiet, and quiet failures compound. Before flipping the switch, you need a way to answer “did every order that should have processed actually process,” and that usually means a reconciliation job running in the background, ironically often on… a schedule. Real-time fulfilment and scheduled reconciliation aren’t opposites. The second one is what makes you trust the first.

A path that doesn’t require a rewrite

You don’t need to tear out the batch job on day one. A practical sequence looks like this: identify the single fulfilment step causing the most business pain, build an event-driven path for that one step alongside the existing batch process, run both in parallel long enough to compare outcomes, then retire the batch step once you trust the numbers. Repeat for the next step if it’s warranted. Most orgs never need to fully retire batch processing everywhere. They end up with a hybrid: real-time where seconds matter, scheduled where they don’t, and a clear, documented reason for which is which.

That’s a more honest answer than “we went real-time” anyway. The goal was never the architecture. It was the customer who used to wait fifteen minutes and now doesn’t.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *