Somebody else's checkout
What a deprecation deadline tells you about which parts of a rented platform were ever yours to begin with.
And a week from today a fair number of merchants are going to find that out the expensive way.
August 13 is when Shopify stops rendering checkout.liquid on the information, shipping and payment steps. Whatever is in that file stops being on the page. This has been on the calendar for well over a year and the emails have been going out the whole time, so nobody can say they weren’t told, and if I had to bet money I’d still say a good number of stores find out on the 14th.
I spent a couple of days last month sitting with one client going through what they’d actually put in there, which is a more interesting exercise than it sounds. The list came to 9 things.
6 of them were decoration. A logo, a typeface, a trust badge, a line of copy above the payment button, some CSS dragging the discount code field somewhere more findable. 2 were tracking pixels. All 8 of those move over in an afternoon, to Checkout UI Extensions and Web Pixels respectively, and a few of them should honestly just be deleted on the way.
The 9th was a fetch at the shipping step that called their ERP to ask what the logged-in customer’s contract price was, because a good half of their revenue is wholesale accounts on negotiated pricing and the storefront price is a polite fiction for those people.
{% comment %} shipping step {% endcomment %}
<script>
fetch('/apps/pricing/contract?customer={{ customer.id }}')
.then((res) => res.json())
.then(applyContractPrice);
</script>
The 9th one was the business. It also does not move over, and there is no version of it that does.
The room was always rented
The framing everybody reaches for is that Shopify took the checkout away. I think that has it backwards. checkout.liquid was a Plus feature that let you paint the walls of a room you were renting. The room stayed theirs the entire time, and the lease was always readable.
The constraint is also most of why the thing converts. Shop Pay already knows a phone number and a shipping address for a large fraction of the shopping public. It fills the form before the customer has finished deciding whether to leave. You are not going to out-build that. I’ve watched two teams try, and both of them got a slower checkout and a very busy incident channel out of the attempt. Handing over control of the checkout is a real cost and it is also, most of the time, the best trade in the whole deal.
I ran into the same wall from the other direction when Liquid wouldn’t let me do a lookup. It took me a week to stop being personally insulted by it. Same fact here, just with a date attached to it.
What’s in the box
Three things replace it. Checkout UI Extensions for interface, which run in a worker against a defined set of APIs. Web Pixels for tracking, sandboxed for the same reason. And Shopify Functions for logic, which is where discounts, shipping rates and payment method rules live now.
A Function is a small program you compile to Wasm. You write a GraphQL query declaring which parts of the cart you want to see, Shopify hands you exactly that. You return a decision. It runs inside the checkout with an instruction budget and a wall clock measured in single-digit milliseconds.
export function run(input) {
const lines = input.cart.lines.filter((line) => line.quantity >= 5);
if (lines.length === 0) {
return { discounts: [] };
}
return {
discounts: [{
targets: lines.map((line) => ({ cartLine: { id: line.id } })),
value: { percentage: { value: "10.0" } },
}],
};
}
input is the whole world. There is no fetch in that file and nowhere to put one, no environment variable holding an API key, nothing to wait on. Even if the network were there you couldn’t afford the round trip inside the budget. So “can I ask my ERP what this customer pays, at checkout” has exactly one answer, and the answer doesn’t get better if you’re a bigger account or you know somebody at the partner org.
Instead you precompute. The contract price gets pushed into the platform ahead of time as a company-level catalog or a customer price list, written by a job that runs whenever the ERP changes its mind. That works. It also means the number on screen is as fresh as your last sync instead of as fresh as your ERP, which is the same shape as an index that’s a copy of your data and lies in the same way when it falls behind. Somebody has to own that difference from now on, and in my experience that somebody gets assigned a few months after go-live, by an incident.
The license is free, like a puppy
The other side of this conversation is the one where you own everything. I’ve spent a lot of years over there. Magento will happily let you make the mid-checkout call. It’ll let you have customer group pricing, tier pricing, attribute sets that mean something, multi-source inventory, tens of thousands of SKUs where the SKUs have opinions. If your commerce is genuinely strange then it will let you be strange, and that is not nothing.
And the license is free the way a puppy is free. You’re paying in salary. A competent developer, plus hosting that isn’t embarrassing, plus the patch cadence, plus the quarterly “why is checkout slow now,” is comfortably a six-figure line item before anybody has built a feature. Every TCO deck I’ve ever been handed compares the license to the subscription, which is comparing the two smallest numbers on the page.
The pitch on the other side underbudgets just as badly, in the opposite direction. You land on the cheap plan, and 18 months later you’re carrying a dozen app subscriptions with several of them over $100 a month, plus a cut of every transaction if you’re not on Shopify Payments, plus Plus if you want Functions doing anything serious. The app tax is real and I’ve never once seen it in the number on the first slide 🙄
I know the counter, which is that you buy the expertise instead of hiring it and a retainer makes the whole thing a wash. Maybe. I’d say (1) a retainer buys you hours and not continuity, and the person who understood your indexer rolls off onto another account, and (2) base rates. Count the self-hosted stores you know that are still on a supported version a few years in, against the ones sitting two minor versions back because the upgrade keeps losing to the roadmap. That’s more or less how a site I built spent two months redirecting phones to somebody’s sweepstakes without anybody doing anything wrong.
For what it’s worth the frontend story over there got a lot better, which people who bounced off it in 2019 tend not to know. Hyvä took Luma out back, and a Tailwind and Alpine theme with a fraction of the JavaScript fixed the single most embarrassing thing about the platform. Mage-OS exists as a community fork with real governance if you’d rather your roadmap wasn’t set by a company that clearly wants you on a SaaS plan. Adobe’s direction is not subtle and I’d weigh it.
Do you have engineers?
So the honest version of the platform question isn’t about feature grids at all, it’s a staffing question, and it’s one people don’t much like asking out loud in a room with the CFO in it. One vendor sells you constraint and takes the operations off your hands. The other sells you control and hands the operations back to you, itemized. Everything else in the comparison is downstream of that single trade, which is also what a hosting move actually is once you stop calling it infrastructure.
All else equal, if your commerce is normal-shaped, products, variants, a cart, maybe a subscription, take the constraint. The flexibility you’re giving up is mostly the flexibility to build a worse checkout than Shop Pay, and the operational relief is worth more than anybody believes until the first Q4 they don’t spend watching a queue back up with the CEO standing behind them.
Take the other one if the weirdness genuinely is the business and you will actually fund the people who hold it up. Not “we’ll hire later.” Later doesn’t come. It’s 2028 and you’re 2 versions back with a patch backlog and everyone’s a little afraid of the deploy, and nobody ever decided that, it just accumulated one quarter at a time.
Every one of these comparisons says the same thing. The platform is almost never why a store does badly. It’s merchandising. It’s the photography, the returns policy, the fact that nobody can find the size chart, the three business days between the order and the shipping confirmation. I once watched a gorgeous headless build get outsold by a stock theme whose product descriptions had obviously been written by a person who had held the product in their hands. The client is going to spend most of this month rebuilding 8 things that didn’t matter and finding a new home for the 1 that did. That’s the right use of the month, and it still isn’t the reason they’ll have a good Q4 or a bad one. My experience skews mid-market and I’ve had two migrations go badly enough to leave a mark, so weigh all of that accordingly.