Shopify's Script Editor was a brilliant hack: a Ruby sandbox stitched into the checkout pipeline, letting merchants express pricing rules in code. It was also the single largest source of production fire-drills on our Plus store. Every campaign needed a developer in the loop; every checkout outage that wasn't infrastructure was usually a Script timing out on a cart with 120 line items.
What Functions actually gives you
A WebAssembly runtime with deterministic limits, typed inputs via GraphQL, and — critically — a config surface merchants can edit without a deploy. We moved from 1,400 lines of Ruby encoding tier pricing and bundle math into ~300 lines of TypeScript plus a Polaris admin where the marketing team self-serves promotions.
The win isn't raw performance. It's that the discount catalogue stopped being code.
Where the abstraction leaks
Functions can't talk to external services mid-checkout. They can't read customer metafields that aren't in the query projection. And their 5ms budget is hard — exceed it and the function is silently skipped, which is worse than failing loudly. We burned a weekend chasing a phantom 'no discount applied' bug that turned out to be an exceeded cart-length iteration.
The fix was unglamorous: bucket the discount logic by cart size, early-return on outliers, and let the cart validator (separate function) catch the edge cases with a clear user-visible reason.
Would I do it again
Immediately. Six months post-migration, Script-timeout pages are zero, and the marketing team ships a promotion in under an hour without Slacking engineering. That's the whole point.
