← Back to Insights
Shopify & E-Commerce·May 25, 2026·6 min read
By Aftab Hussain, Founder

What actually breaks in a Shopify Plus checkout at scale

The four failures we find most often on high-volume Shopify Plus checkouts, and the first-sprint fixes for each.
What actually breaks in a Shopify Plus checkout at scale

A checkout that handles 200 orders a day and a checkout that handles 2,000 are not the same piece of software. The second one fails in ways the first one never hinted at, and almost none of those failures are about traffic.

Shopify absorbs the load. That part is genuinely solved, and it is the reason most brands stop thinking about the checkout as engineering at all. What Shopify does not absorb is everything a growing store bolts onto the order path over three or four years: the subscription app, the loyalty widget, the tax connector, the two analytics pixels nobody can name an owner for, and the custom logic somebody wrote against an API that has since been deprecated.

None of that accumulation was a mistake at the time. Each app solved a real problem on the day it was installed, usually faster and cheaper than building the thing properly. The cost is deferred rather than avoided, and it comes due at the point where order volume makes every small inefficiency legible in the numbers.

Those are the parts that break. Below are the four we find most often, in the order they usually surface.

1. Third-party scripts sitting on the critical path

Every app you install asks for a small amount of the customer's attention span, and it takes it from the same budget. Individually each one is defensible. Collectively they decide whether the payment button is interactive in one second or in four, and the difference shows up in your conversion rate long before it shows up in a bug report.

Nobody owns this metric, which is why it drifts. Marketing owns the pixels, growth owns the experimentation tool, support owns the chat widget, and none of them are looking at time to interactive on the payment step. The number gets worse by two hundred milliseconds a quarter and no single decision caused it.

The measurement that matters is not page weight. It is how much of the delay sits in front of the customer's ability to pay. On a store we audited last quarter, 2.4 seconds of a 3.9-second interaction delay came from three scripts, and two of them were rendering nothing on the checkout at all.

Mobile is where this lands hardest. A mid-range Android phone on a normal connection does not have the CPU headroom of the laptop the store was tested on, and script execution costs three to four times more. If your traffic is majority mobile, the desktop measurement is not a conservative estimate of the problem, it is a different problem entirely.

TIME TO INTERACTIVE PAYMENT BUTTON · REAL AUDIT
Shopify core
1.5s
Loyalty widget
1.1s
Session recorder
0.8s
Legacy A/B tool
0.5s
TOTAL BEFORE3.9s
AFTER MOVING TWO TO WEB PIXELS1.9s
Two of the four scripts rendered nothing on the checkout. They were measurement, not experience.

The first question to ask about any script on the order path is whether a customer would notice its absence. Measurement tools fail this test by design: they exist to observe, and observation should never be able to delay a purchase. That single question resolves most of the inventory without any technical judgement at all.

The fix is usually reclassification rather than removal. Anything that only observes belongs in the Web Pixels sandbox, where it runs off the critical path and cannot block rendering. Anything that must render belongs in a checkout UI extension with a defined footprint. Anything that does neither should be uninstalled, and the reason nobody will uninstall it is that nobody is sure what it does. Finding out is a two-hour job that routinely returns a second of latency.

Removal has a political dimension worth naming. Every one of those apps was championed by somebody, and uninstalling it reads as a verdict on their decision. Framing the exercise as an inventory rather than a cull helps, because the output is a list with owners and costs attached, and the owners get to make their own case with the number in front of them.

2. Treating the extensibility migration as a port

Checkout customisation through checkout.liquid ended for the information, shipping and payment steps in August 2024, and for the thank-you and order-status pages a year later. Most brands have moved. A good number moved by asking a developer to reproduce the old markup as closely as possible, which is the expensive way to do it.

The instinct is understandable. A checkout that looked a particular way for four years accumulates internal expectations, and reproducing it exactly is the shortest path to nobody complaining. It is also the path that carries every accumulated workaround into a system that no longer needs them.

The two models are not equivalent. The old one let you write arbitrary markup into a page Shopify owned. The new one gives you defined extension points, server-side Functions for logic, and a sandbox for everything else. Logic that used to live in template conditionals belongs in a Function; presentation that used to be inline markup belongs in an extension. Ported one-to-one, you get the constraints of the new model with none of its benefits, and a checkout that is harder to change than the one you left.

The practical test for a completed migration is whether a merchandising change requires a developer. If a seasonal discount rule still means editing code and deploying, the logic did not move into Functions, it moved into an extension pretending to be a template. Done properly, the same change is a configuration edit and the developer is not involved.

WAS: CHECKOUT.LIQUID
NOW: WHERE IT BELONGS
Discount rules in template conditionals
Shopify Function, server-side
Custom trust badges and copy
Checkout UI extension
Analytics and pixel injection
Web Pixels sandbox
Shipping method filtering
Delivery customisation Function
Migrating well means re-homing each behaviour, not reproducing the old markup.

There is an upside worth mentioning, because the migration is usually framed as a compliance exercise. Functions run server-side and cannot be manipulated from the browser, extensions are sandboxed and cannot break the rest of the page, and the checkout stays upgradeable as Shopify ships changes. A well-executed migration ends with a checkout that is measurably faster and considerably harder to break than the one it replaced.

3. Cart state that disagrees with itself

This one produces the support tickets that are hardest to reproduce. A customer adds a bundle, a subscription app rewrites the line items, an upsell fires a second add request before the first resolves, and the cart that reaches the checkout is not the cart the customer built. Quantities double. A discount attaches to the wrong line. A subscription selling plan silently drops.

These tickets are expensive out of proportion to their frequency. They arrive as a screenshot from a customer, they cannot be reproduced on request, and they consume a developer for half a day each time. Because they are intermittent, they rarely get prioritised, so they persist for years and quietly train the support team to treat cart complaints as user error.

The underlying cause is almost always concurrent writes to the cart from code that assumes it is the only writer. Two apps and one theme customisation all calling the cart endpoints on the same interaction is enough.

The failure is timing-dependent, which is why it disappears when you look for it. On a fast connection the requests resolve in order and the bug does not exist. On a slow one, or on a phone waking from sleep, or when one of the apps is having a slow day, the order changes and the last write wins. Your customers on poor connections experience it constantly and your team never does.

ONE TAP ON "ADD BUNDLE" · FOUR WRITERS
t+0msTheme posts three line itemscart/add
t+40msUpsell app appends a fourthcart/add
t+60msSubscription app rewrites all line items from a stale readcart/change
t+95msDrawer renders whichever response landed lastcart.js
Nothing here is a bug in isolation. The defect is that four writers share one cart and none of them coordinate.

Diagnosing this does not require sophisticated tooling. Logging every cart mutation with a timestamp and a source for a single day is usually enough to see the pattern, because the interleaved writes are obvious once they are in one list ordered by time.

The fix is a single mutation queue in the theme that every cart write passes through, with the drawer rendering only from the response of the most recent completed write. It is perhaps 150 lines. It ends an entire category of ticket.

The harder part is enforcing it. A queue only works if every writer goes through it, which means auditing the apps that bypass the theme entirely and either configuring them to defer or accepting that one of them has to go. That conversation is easier once the ticket volume has a cause attached to it.

4. Promotional logic that outgrew the tools

Stacked discounts, tiered volume pricing, market-specific shipping rules and a loyalty balance all want to modify the same total. Shopify Functions handle each of these well and are the correct home for the logic. What Functions do not do is tell you what the interaction between four of them should be when they all apply to one order.

The interaction rules are where the real complexity sits, and they multiply rather than add. Four independent mechanisms produce sixteen combinations, most of which nobody has ever considered, and the ones that matter are the edge cases where a customer finds a stack that charges less than you intended.

That is a merchandising decision, and it needs to be written down before it is implemented. When it is not, the answer ends up encoded across three apps and one Function, and no one on the team can predict what a given basket will be charged. We have twice been asked to debug a discount stack where the correct behaviour had never been agreed in the first place.

The fix in both cases was not code. It was a table, agreed between merchandising and finance, stating which discounts stack, which are exclusive, and what the floor margin is. Implementing the table afterwards took a day. Agreeing it took two weeks, which is the honest reason it had never been done.

Most checkout work that gets described as a rebuild is really an inventory problem: nobody has a current list of what runs on the order path, and why.

What the first sprint usually covers

We start with the inventory, because it is cheap and it reorders the priority list nearly every time. Every script on the order path, what it does, who owns it, and what it costs in milliseconds. From there the first week normally covers reclassifying the observers into Web Pixels, putting a mutation queue in front of the cart, and writing down the discount interaction rules as they currently behave rather than as anyone believes they behave.

That last distinction does more work than it sounds like. Documenting current behaviour is a mechanical task that produces an artefact everyone can argue with. Documenting intended behaviour is a negotiation, and starting the negotiation without the current state on the table is how these projects stall.

None of that is a rebuild, and none of it needs a discovery phase. On the Pipers Farm checkout the same sequence moved cart abandonment down 10 percent, and the largest single contributor was deleting things.

If you take one thing from this, do the inventory. It requires no engineering commitment, it can be done by anyone with access to the theme and the app list, and it reliably surfaces at least one script nobody can justify. That is a free improvement to the most valuable page you own.

Want the inventory done on your checkout?
One scoping call with the engineer who would do the work. Commerce Lab sprints start at $800.
Book a scoping call
Share this article:
Aftab Hussain

Written by Aftab Hussain

Founder, CTO and Product Architect at The DaaS Labs. Scopes the sprints, owns the architecture, and still reviews the code.

Connect on LinkedIn →

Whatever's blocking you, it moves next sprint.