Shopify Functions look tiny: a WASM binary, an input query, a run function. They sit on the most sensitive path in commerce: the path that decides whether money and inventory move. I have shipped discount generators, cart transforms (including tip price adjustments), and purchase validation (including credit limits). The constraints are the curriculum.
Why the sandbox is so strict
Functions cannot casually call your API. They operate on a queried subgraph of cart/checkout state with tight CPU and size limits. That design protects storefront performance and platform safety. It also means some business rules simply do not fit.
If your rule needs a live credit balance from an ERP on every keystroke, you must design a hybrid: UI/extension for experience, precomputed metafields or recent sync for Function inputs, server systems for source of truth, and clear failure behavior when data is stale.
Input queries are architecture
The GraphQL input query is not a detail. It is your contract with Shopify. Over-fetch and you may blow budgets. Under-fetch and you invent impossible logic. Changing an input query is a compatibility event for every merchant install.
I treat input queries like public APIs:
- version thoughtfully
- keep them readable
- document which fields are load-bearing
- test with realistic cart shapes, not toy carts
Fail closed vs fail open is a business decision
Checkout validation that blocks progress when credit is exceeded is fail closed, and correct for wholesale risk. A discount Function that fails open may quietly stop applying promotions, and merchants notice as revenue mysteries.
Engineering must force product owners to choose. “Return an error” and “return no discounts” are different products.
Composing UI + Function is where bugs breed
Tip UI writes an attribute; cart transform adjusts a line price. Delivery UI collects a date; validation ensures presence. Subscriber discount Function applies line discounts based on customer state.
If UI and Function disagree about attribute names, namespaces, or units (cents vs dollars, inclusive tax assumptions), you get carts that look right in UI and wrong at payment, or the reverse.
Shared constants, contract tests, and a single glossary beat tribal knowledge.
Local DX vs production truth
Function development tooling has improved, but production carts have gift cards, subscriptions, drafts converted to orders, B2B catalogs, and POS-originated quirks. Test matrices expand quickly. Budget time for ugly carts.
What I document for every Function
- Business rule in one sentence
- Fail closed or fail open
- Required input fields
- Attribute/metafield contract with any UI extension
- How merchants observe that it is working
- How we roll back (previous Function version / disable path)
Functions are small artifacts with large blast radii. Respect the radius.
