A senior engineer's deep dive into Shopify Functions in 2026. The five extension points, what each one solves, when Functions beat apps, when apps beat Functions, and how Functions fit alongside headless storefronts.

Three years after Shopify Functions launched, most developers we talk to still describe them as "that thing for custom discounts." It is true that custom discounts is what Functions ship most commonly, and it is also missing the larger point: Functions are the cheapest path to custom commerce logic that Shopify has ever offered, and the merchants who understand the full range of extension points ship competitive advantages that custom apps once required.
This post is the deeper engineering view of Shopify Functions in 2026. It is the post that should accompany our custom app vs public app decision framework earlier this month, because Functions are often the right answer to a problem that custom apps used to claim. It also addresses the question that comes up on every Plus engagement: how do Functions fit alongside headless storefronts, where the front-end runs outside Shopify's runtime but the checkout still depends on Shopify's logic.
Functions are pieces of custom logic, written in Rust, JavaScript, or TypeScript, compiled to WebAssembly, and deployed into Shopify's runtime. They run synchronously at specific extension points in the cart and checkout, with strict execution-time budgets and a deterministic input-output contract. They are not webhooks. They are not server-side code you host yourself. They are not theme extensions. They are platform-native logic that becomes part of how Shopify processes commerce.
The architectural significance of that distinction: a Function runs with the order, not after it. A cart transform Function modifies what the customer sees in the cart in real time. A discount Function applies before the totals render. A delivery Function determines what shipping rates are shown. These are not asynchronous reactions to events; they are synchronous decisions inside Shopify's commerce flow.
The implications for app strategy:
In 2026, Functions ship for five primary extension types. Each solves a different problem and each has its own constraints.
The original Function category and still the most-used. Replaces Shopify's automatic discount system for any logic that the native discount engine cannot model.
What it can do: Conditional discounts based on cart contents, customer attributes, or metafields. Tiered pricing where the discount changes based on quantity bands. Custom "buy X get Y" logic where the system's native variant cannot capture the rule. Customer-tier discounts that read from a metafield on the customer (loyalty status, B2B tier, contract pricing).
What it cannot do: Calculate based on data outside Shopify (no HTTP calls during execution). Maintain state across cart updates. Apply manually-triggered discounts (Function discounts are automatic by definition).
When it wins versus an app: When the discount logic is rules-based and deterministic from cart state. A "buy two of category A, get one of category B free" rule that Shopify Native cannot model is a Function. A discount tier based on customer metafields is a Function.
When an app wins: When the discount logic requires real-time external data (live competitor pricing, dynamic surge pricing). When the merchant needs a UI for non-technical staff to manage discount rules (Functions ship via code deployment; merchant-managed rules need an app with a dashboard).
Modifies what shipping options the customer sees at checkout. Hide, rename, sort, or modify shipping methods based on the cart contents, customer attributes, or order value.
What it can do: Hide express shipping for orders containing oversized items. Sort shipping options by price ascending or by delivery date. Rename shipping options based on the destination country. Apply free shipping thresholds with non-trivial logic (free shipping only on certain product categories above a certain spend).
What it cannot do: Add new shipping rates that do not exist as carrier-calculated or manually configured rates. Modify the actual rate values (a separate Function type, "delivery method customization," is in progress at Shopify but limited). Call external shipping APIs during execution.
When it wins: When the logic is "show or hide existing rates based on cart state." Replaces a category of apps that did exactly this through fragile checkout script injection.
When an app wins: When you need carrier-calculated rates from a non-supported carrier, which still requires an app integrating with the Shopify Carrier Service API.
Modifies what payment methods the customer sees at checkout. Hide, rename, sort, or modify payment options based on the cart contents.
What it can do: Hide credit card payment for orders above a certain value (forcing wire transfer for high-value B2B orders). Hide buy-now-pay-later options for restricted product categories. Rename payment options for B2B accounts. Sort payment methods to surface the highest-margin option first.
What it cannot do: Add new payment methods (payment gateway integrations are still apps). Reject orders based on payment method (use a Function in cart transform to do this earlier).
When it wins: When the logic is "show or hide existing payment options." B2B stores routinely use this to enforce payment terms based on customer tier or order value.
When an app wins: When you need new payment method integrations or complex fraud detection that requires external data.
The most powerful and most complex Function type. Modifies the cart contents themselves before discounts and totals are calculated. Replaces a category of work that previously required either custom apps with cart API mutations or fragile JavaScript that fought Shopify's native cart.
What it can do: Bundle products into a virtual single line item (a bundle that appears as one line at one price but contains multiple SKUs for inventory and fulfillment). Expand a single line into multiple lines (the inverse: a "starter kit" bundle that decomposes into its components on add-to-cart). Add free gifts based on cart contents (the "buy this, get free X" pattern, but as actual cart line items rather than a discount). Update line item properties based on cart-level logic.
What it cannot do: Read external data during execution. Operate across multiple carts (each Function call sees only the current cart). Modify customer or shipping address.
When it wins: Bundle logic that the native bundle product type cannot model. Free gift logic that needs to appear as actual line items rather than discounts. Any cart manipulation that previously required cart API mutations from a hosted app.
When an app wins: When the bundle composition needs to come from an external system (a configurator that builds custom kits based on user input not in Shopify). When the logic requires UI for the customer to interact with mid-cart.
The newest Function category. Enforces rules about what can ship together based on inventory location, product attributes, or order configuration.
What it can do: Prevent fragile and heavy items from shipping in the same parcel. Route fulfillment to the closest warehouse based on shipping address. Block orders that cannot be fulfilled from any single location.
What it cannot do: Call a warehouse management system during execution. Override carrier shipping decisions.
When it wins: When the fulfillment logic is rules-based and deterministic from order state.
When an app wins: When the logic requires real-time inventory data from a non-Shopify source or complex routing that needs an external optimization engine.
The framework for choosing among these three paths for a given commerce logic problem:
The framework dramatically narrows the "what tool" question. The right answer is often "Function for the rules engine, app for the merchant UI, headless storefront for the customer-facing presentation, and they all coexist."
To illustrate how the pieces fit together, the architecture for a recent B2B Shopify Plus client at Sentinu:
Three Functions handle the commerce logic at checkout. One custom app handles the merchant-facing administration (because sales managers need to update B2B tier assignments without engineering involvement). A Hydrogen storefront handles the customer-facing UX with branded design and the B2B catalog navigation.
Each piece does what it does best. Functions for fast, deterministic, in-checkout logic. App for the admin UI that non-developers need to operate. Headless for the customer presentation that needed brand-specific design.
We covered the B2B portal architecture in detail in our Shopify Plus B2B post; this is the same architecture with the Function layer made explicit.
Honest cost ranges for Function development engagements:
| Project shape | Cost |
|---|---|
| Single discount Function (rules-based, modest complexity) | $3K to $6K |
| Single delivery customization Function | $3K to $5K |
| Cart transform Function with bundle logic | $6K to $12K |
| Suite of 3 to 5 Functions for a B2B portal | $15K to $30K |
| Function plus admin app for merchant configuration | $20K to $50K |
The variance is in the input data model and the testing surface. Functions are stateless and well-typed, which makes the code itself fast to write. The work is in defining the metafield schema, testing the edge cases, and verifying the Function does not regress on existing orders.
Ongoing maintenance is minimal compared to apps. A Function does not have a hosted backend to maintain, no API key rotation, no server outages. The main maintenance concern is the Shopify API version that the Function targets, which deprecates on the same 12-month cycle as the rest of the Shopify platform.
The biggest under-appreciated benefit of Functions versus apps is the operational simplicity. An app that handles discount logic for a Shopify store is a service you must monitor, update, secure, and pay to host. The equivalent Function is a compiled WebAssembly module that Shopify runs on your behalf. The total cost of ownership over three years is dramatically lower for Functions, and the failure modes are dramatically simpler.
The honest counter-cases. Functions are not the right answer to every commerce problem.
When you need a merchant-facing UI. Functions have no admin interface. The merchant configures them through metafields, which is acceptable when the merchant is a developer or working with a dedicated agency but unacceptable when the merchant wants to change a discount rule from the Shopify admin without filing a ticket. For merchant-configurable logic, you build an admin app on top of the Function (the architecture pattern from the B2B example above).
When the logic requires real-time external data. Functions have no network access during execution. If your discount rule needs to call a competitor-pricing API in real time, you cannot use a Function alone. You either pre-compute the data and store it in metafields that the Function reads (good for daily-refreshed data) or you use an app with a hosted backend (good for true real-time).
When you need merchant-managed configuration with high frequency. A Function deployment is a code deployment. A merchant who wants to change a rule daily should not be deploying Functions daily. The same Function plus a metafield-driven configuration solves this, but it means design work to define the configuration schema.
When the merchant has zero engineering capacity. Functions are code. Someone has to write them, test them, and maintain them. For a merchant whose team is entirely non-technical, the app store path remains correct because the maintenance cost is the app vendor's problem, not the merchant's.
Recent Function engagements illustrate the range:
The pattern: Functions are the answer when the logic is rules-based, deterministic, and lives at checkout. They are not the answer when the logic needs UI, external data, or operational flexibility that only an app provides. The architecture is usually some mix of the two, and the discipline is knowing which part goes where.
No, not entirely. Discount Functions and delivery customization Functions are available on all plans. Payment customization Functions and cart transform Functions are Plus-only. Fulfillment constraint Functions are Plus-only. For non-Plus stores, the available Function categories cover the most common use cases.
Rust is the official primary language and the most-supported in the documentation. TypeScript and JavaScript are also supported and ship to the same WebAssembly target. For most teams, JavaScript or TypeScript is the right call because the hiring pool is larger and the Shopify SDK examples cover them well. Rust is the better choice for teams that already use Rust elsewhere or for Functions that need maximum performance.
Flow is asynchronous; Functions are synchronous. Flow runs after events happen (order placed, customer created); Functions run during the event (cart updated, checkout viewed). They complement each other. A Function can apply a discount during checkout; a Flow can send the customer a thank-you email after the order completes. We covered Flow indirectly in our workflow automation comparison, where the same operational principles apply.
Yes. Shopify ships a CLI that runs Functions against sample inputs locally. The development loop is similar to writing serverless functions in other ecosystems: write, test against fixtures, deploy, verify in a development store. Production-grade Function development at our scale uses CI/CD that runs the Function against a battery of test cases before deploying.
Strict. Functions must complete within a small number of milliseconds (the exact number varies by Function type but is generally under 50ms). Functions that exceed the budget are killed and the merchant's checkout proceeds without the Function's logic, which is often a worse experience than the merchant designed. This is why Functions cannot make network calls; the latency budget would be exceeded by any external dependency.
Yes. Each Function deployment creates a new version. Older versions remain associated with their deployment date and can be rolled back to. This is one of the operational advantages over apps, where rolling back to a previous version often requires coordinating with the app vendor.
No. They solve different problems. Flow is the asynchronous workflow tool for post-event automation. Functions are the synchronous extension points inside Shopify's commerce flow. A modern Shopify Plus store typically uses both.
If you are scoping a commerce logic problem and Functions look like the right path but you want validation against your specific business shape, that is what our Shopify app development practice covers. We have shipped Functions, custom apps, and headless storefronts in every combination, and we will tell you honestly which combination fits the problem. If the broader question is whether Functions, custom apps, or headless commerce is the right architectural fit for your store, our custom app vs public app post and Hydrogen vs Next.js post are the companion reads. The right architecture for a modern Shopify Plus store is rarely one of these tools; it is the right combination of all three.

A senior engineer's decision framework for Shopify app strategy in 2026. When to build a custom app, when to install from the App Store, when to extend an existing app, and the real cost math behind each path.

June 30, 2026 is a hard wall. Scripts editing is already locked. If your checkout discounts, shipping rules, or payment logic still run on Scripts, here is the migration playbook, the failure modes, and what it costs.

Metafields attach data to existing resources. Metaobjects are standalone records you can reference anywhere. Here is when to use which, how to model them, and the API patterns that scale across thousands of products.