Case notes

Case note: order capture from 13 seconds to sub-second

TL;DROrder capture is slow when every line synchronously calls the ERP to price and check stock. Move that work off the hot path: precompute price and availability per outlet, cache at the edge, serve the catalogue from snapshots, and validate asynchronously. In our instrumentation median capture latency fell from the low teens of seconds to under a second.

Order capture crawled because every line a rep added fired a synchronous call to the financial core to price it and confirm stock. We moved that work off the hot path: price and availability precomputed per outlet, cached at the edge, the catalogue served from snapshots, validation run asynchronously. In our instrumentation, median capture latency fell from the low teens of seconds to under a second. Financial postings stayed async, and that was deliberate.

Why did a single order take thirteen seconds?

The public benchmark for this pattern is unambiguous. The world's largest brewer rebuilt its B2B ordering platform in-house and reported that tasks which had sometimes required as many as 13 seconds now return in sub one second. We have rebuilt the same shape for an unnamed brewer more than once, and the diagnosis rarely changes.

A legacy commerce stack treats the ERP as the live source of truth for everything inside the ordering flow. It prices each line live, checks stock live, validates credit live, and it does all of this synchronously, on the rep's click. Each of those is a round trip to a system engineered for month-end correctness, not for someone standing in a shop with a phone and a queue behind them. Add load — a promotion, a payday, a route running late — and the round trips queue behind each other. The seconds stack up in the tail, exactly where they cost you the order.

Pricing is usually the worst offender, because it is the most conditional: outlet-specific lists, deals, excise and taxes. In one public account, the pricing engine alone carried an eight-second delay before it was brought down to around 100 milliseconds. That single component can dominate a capture flow before anyone has checked a single case of stock.

What actually moved the needle?

Precomputation. The load-bearing insight is that price and availability for a given outlet change on a schedule you control, not on every click. So compute them ahead of time, per outlet, and serve them from a read model cached close to the edge. The catalogue is served from immutable, versioned snapshots so the screen renders instantly and you invalidate on your terms. Validation — credit, minimum order, returnables balance — runs asynchronously: accept the order optimistically onto the bus, validate downstream, surface the rare exception.

In our own instrumentation, median capture latency fell from the low teens of seconds to under a second, but the median was never the interesting number. The tail is where reps abandoned a basket and reached for the old spreadsheet, so p95 and p99 are where the conversion effect actually lives. Bringing the worst case under a second is what changes behaviour on the ground.

The table below is the whole trick in one view: nothing that must be correct at month-end belongs on the click.

OperationLegacy: synchronous, on the clickRebuilt: off the hot path
PricingLive call to the ERP per linePrecomputed per outlet, cached at the edge
AvailabilityLive stock check per linePrecomputed with a staleness budget, reserved-stock signal on the bus
CatalogueRead live from the coreImmutable, versioned snapshots
Credit and limitsSynchronous validation before acceptOptimistic accept, async validation, exception flagged
Financial postingOn or near the clickAsync and batched, never on the click

The same public rebuild shows what that headroom buys at scale: the brewer went on to launch in a new country in as few as ten weeks against the 8+ months the old versions took, and to carry over 1.6 million orders a week at more than $5.5B in gross merchandise value for the quarter reported. A synchronous-ERP architecture cannot serve those volumes at a latency a rep will wait through. All of it rides one event bus, which is the part of our architecture that makes precompute-and-validate-later safe rather than reckless.

How to move order capture off the ERP hot path

The work is more disciplined than dramatic. In order:

  1. Instrument the hot path before touching it. Measure p50, p95 and p99 of the capture flow and attribute the milliseconds to each synchronous call. Optimise what the numbers show, not what you assume.
  2. Precompute price and availability per outlet. Recompute on a schedule you own, publish to a read model, and key it by outlet and SKU so a lookup is a single cache hit.
  3. Serve the catalogue from immutable snapshots. Version them, render instantly, and invalidate deliberately rather than reading the core on every screen.
  4. Make validation asynchronous. Accept the order onto the event bus, then validate credit, minimum order and returnables downstream, reconciling exceptions instead of blocking the accept.
  5. Keep financial postings async and batched. The click captures the order; finance is posted later. This is exactly how we replaced the commerce layer without touching SAP FI — the core stays authoritative for money, and nothing on the hot path waits for it.
  6. Set a staleness budget and a reconciliation path. Decide up front how stale price and stock may be, tighter for fast movers, and define what happens when the async check disagrees with the optimistic accept.

What we got wrong the first time

Optimistic accept plus an edge cache means that, occasionally, you will take an order for stock that is not there. Our first cut set too loose a staleness budget on availability, and during a promotion the cache happily reported cases as available that the depot had already committed. We accepted orders we could not fill, and the reconciliation landed on a customer-service team rather than on the algorithm that caused it.

The fix was not to reinstate synchronous stock checks and give back the seconds. It was a tighter staleness budget on fast-moving SKUs, a reserved-stock signal published on the bus at the moment of accept, and surfacing the exception to the rep within seconds instead of at delivery — much like the live feedback in the telesales console that replaced printed call sheets. Keeping financial postings async and batched was the right call, but it means the order is captured and the order is posted to finance are two different timestamps. Our first dashboards conflated them; getting the reporting layer honest about which timestamp it is showing took a second pass.

Sub-second capture is not the finish line; it is the floor that makes the interesting work possible. Once an order is on the bus in under a second, the headroom goes toward what runs alongside the rep rather than in front of them: real-time deal eligibility, returnables netted at the point of capture, suggested-order models that score a basket while the call is still live. We are wiring those next, and each one earns its place only if it stays off the hot path.

Frequently asked questions

Won't asynchronous validation let bad orders through?

Rarely, and by design you catch them fast. The order is accepted onto the event bus, then credit, minimum-order and returnables checks run downstream within seconds and flag exceptions. You trade a synchronous block for a fast async catch plus a reconciliation path, far cheaper than making every rep wait on the ERP.

Do we have to replace SAP to get sub-second order capture?

No. Keep SAP as the authoritative financial core and strangle only the commerce layer around it. Pricing, availability and the catalogue move to precomputed read models; financial postings stay asynchronous and batched. The click captures the order, and nothing on the hot path waits for a posting to the core.

How stale can precomputed price and stock be?

You set a staleness budget per data type, tighter for fast-moving SKUs than for slow ones. Pair it with a reserved-stock signal published when an order is accepted, so the cache reflects committed volume. When the async check disagrees with the optimistic accept, a reconciliation path resolves it before delivery, not at the dock.

Stuck with exactly this?

BrewOS builds the full route-to-market stack for global brewers — order capture, stock, loyalty, returnables, delivery and analytics on one event bus, run by a team of ~20 engineers. Bring us the feature that’s been stuck the longest and we’ll show you how we’d ship it in days.

Book a 30-minute walkthrough