All Posts
Commerce Engineering September 18, 2026

Shopify Functions in Rust vs JavaScript: WebAssembly Performance and When Each Wins (September 2026)

Shopify Functions can be written in any language that compiles to WebAssembly, and the two most common choices, Rust and JavaScript, are not close on performance. JavaScript Functions run about three times slower than Rust, because the JavaScript runtime itself has to be bundled into the Wasm module, while Rust and AssemblyScript compile more or less directly to Wasm instructions. That gap would be academic if Functions had generous limits, but they do not: Shopify enforces an 11 million instruction budget per invocation, and blowing past it means Shopify rejects your output and falls back to default behavior. For simple discount rules the language does not matter. For complex logic over large carts, it decides whether your Function runs at all. This is the trade-off, with real numbers and a concrete pattern for staying inside the budget.

Shopify Functions in Rust vs JavaScript: WebAssembly Performance and When Each Wins (September 2026)

Shopify Functions run as compiled WebAssembly modules inside Shopify's own infrastructure, and they accept any language that compiles to Wasm: Rust, AssemblyScript, TinyGo, and JavaScript are all supported. The temptation for most teams is to reach for JavaScript, because the team already knows it. That is a reasonable default for simple logic and a costly one for complex logic, because JavaScript Functions run roughly three times slower than Rust, and Shopify caps every invocation at 11 million instructions. When your logic is heavy, that combination decides whether the Function executes or silently reverts to Shopify defaults.

Language Wasm output Relative speed Best fit
Rust Compiles close to native Wasm Fastest, Shopify's recommended default Complex logic, large carts, tight budgets
AssemblyScript Compiles close to Wasm, TypeScript-like syntax Near Rust Teams wanting familiar syntax without the JS runtime
JavaScript JS runtime bundled into the module About 3x slower than Rust Simple rules, small carts, fast iteration

Why JavaScript Is Slower

The reason is structural, not a matter of tuning. Languages like Rust and AssemblyScript compile your source into Wasm instructions directly, so the module Shopify runs is essentially your logic. JavaScript cannot do that, because it is a dynamic language that needs an interpreter. To run JavaScript as Wasm, the module has to include a JavaScript engine, and every invocation pays the cost of that engine starting up and interpreting your code on top of executing it. You are shipping a runtime and your logic, where Rust ships only your logic.

That overhead is fixed cost you pay on every call before your business rules do any work. For a Function that reads three cart fields and returns a discount, the overhead is invisible and JavaScript is completely fine. For a Function that iterates over 200 line items applying a tiered discount matrix, the overhead plus the per iteration interpretation cost can push you toward the instruction ceiling, and that is where the language choice stops being a preference.

The 11 Million Instruction Budget

Shopify measures each Function invocation in instructions and enforces a limit of 11 million per call. Exceed it and Shopify does not truncate or warn: it rejects the Function's output entirely and applies default behavior for that invocation. In practice that means a discount that does not apply, a shipping rate that does not hide, or a payment method that does not gate, which is the same failure you get from no Function at all. This is the constraint that makes performance a correctness issue rather than a nicety, and it is the most common reason a Function that passed on a small test cart fails in production on a large one.

The budget is per invocation, and cart size is the variable that moves it most. A rule that costs 40,000 instructions per line item is comfortable at 50 items and over budget at 300. Because you do not control how large a customer's cart gets, you have to design for the worst case cart, not the average one.

The Pattern: Push Work Into the Input Query

The most effective way to stay inside the budget is counterintuitive: do less in your code by asking Shopify to do more before your code runs. Every Function declares an input query in a run.graphql file. Shopify executes that query first and passes your module the resulting JSON, so any filtering, selection, or scoping you can express in the query is work that never touches your instruction budget.

A minimal input query that fetches only the fields a discount rule needs looks like this:

query Input {
  cart {
    lines {
      quantity
      merchandise {
        __typename
        ... on ProductVariant {
          id
          product { hasAnyTag(tags: ["sale"]) }
        }
      }
    }
  }
}

By requesting hasAnyTag in the query rather than fetching every tag and filtering in code, you move the tag check to Shopify's side and hand your module a boolean. The Rust target that consumes it stays small:

#[shopify_function]
fn run(input: input::ResponseData) -> Result<output::FunctionRunResult> {
    let discounts = input.cart.lines.iter()
        .filter(|line| line.merchandise.on_sale())
        .map(build_discount)
        .collect();
    Ok(output::FunctionRunResult { discounts, ..Default::default() })
}

The rule of thumb is: fetch narrowly, compute minimally, return early. Fields you filter in the query cost you nothing at runtime. Fields you fetch broadly and filter in code cost you instructions on every line item. This pattern matters in any language, and it is what lets a well written JavaScript Function survive workloads that a naive Rust Function would fail, because algorithm and data access dominate the budget more than language does until the logic gets heavy.

Choosing Per Function, Not Per Team

The right decision is per Function, not a blanket team standard. Audit each Function by its worst case input. A payment gating Function that checks one property against a small allowlist is fine in JavaScript and faster to ship. A discount Function that evaluates a combinatorial promotion across a large cart should be Rust or AssemblyScript, because the headroom against the 11 million instruction ceiling is the difference between it working on Black Friday and reverting to defaults at the worst possible moment. This is the implementation companion to the broader rebuild sequencing we laid out in our Shopify Scripts to Functions migration playbook, and it attaches to the same checkout extension points we covered in our checkout extensibility guide.

For teams sizing the cost of maintaining two languages against the cost of failed invocations, it is worth folding this into the wider platform economics rather than treating it as an isolated engineering call. We work through the total cost picture for Plus in our SFCC versus Shopify Plus TCO analysis.

When This Applies to Your Stack

If your Functions were written quickly during the Scripts migration and all default to JavaScript, profile the ones that touch discounts and shipping under a large cart before your next peak sales event. Any Function that clears the budget comfortably at 50 items but approaches it at 300 is a candidate to rewrite in Rust or to refactor so more work lives in the input query. The goal is not to rewrite everything in Rust; it is to ensure that the Functions carrying real revenue have the headroom to run on your largest carts.

Contra Collective builds and hardens Shopify Functions for Plus merchants, including profiling against worst case carts, rewriting hot paths in Rust, and restructuring input queries to reclaim instruction budget. If your checkout logic is a mix of quickly shipped JavaScript Functions and you want confidence they will hold under peak load, we can audit and rebuild the ones that matter.

FAQ

Is Rust always the right choice for Shopify Functions?

No. Rust is the safest choice for complex logic over large carts because it leaves the most instruction budget, but for simple rules JavaScript is fine and faster to ship. Decide per Function based on the worst case input.

How much slower is JavaScript than Rust for Functions?

About three times slower, because the JavaScript runtime has to be bundled into the Wasm module and interpret your code at execution time, where Rust compiles close to native Wasm instructions.

What is the instruction limit and what happens if I exceed it?

Shopify allows 11 million instructions per Function invocation. If you exceed it, Shopify rejects the Function's output and applies default checkout behavior for that call, which looks like your logic silently not running.

Does the input query count against the instruction budget?

The GraphQL input query is executed by Shopify before your module runs, so filtering and selection done in the query does not consume your instruction budget. Moving work into the query is the primary way to stay under the limit.

Is AssemblyScript a good middle ground?

Yes. AssemblyScript gives you TypeScript-like syntax while compiling close to Wasm without bundling a JavaScript runtime, so it performs near Rust and is a reasonable option for teams that want familiar syntax with better headroom than JavaScript.

Sources

[ 02 ] — Keep Reading

More from the lab.

Sep 29, 2026 Commerce Engineering

The Cloud Security Alliance's Agentic Transaction Security Framework: A Compliance Blueprint for AI Checkout

Agentic checkout is shipping faster than the compliance infrastructure meant to govern it. Shopify defaulted agent checkout on for a million merchants, Stripe and OpenAI's Agentic Commerce Protocol is live, and Google extended Universal Cart with holiday specific checkout tooling this month, all while the Cloud Security Alliance's Agentic Transaction Security Framework, structured around five control areas including explicit mapping to PCI DSS v4.0.1 and PSD2/PSD3 strong customer authentication, remains the clearest attempt to define what secure actually means for a transaction an agent initiates rather than a human clicking buy. This post breaks down what the framework actually requires and where it intersects with the agentic checkout paths your storefront is probably already exposed to.

Sep 18, 2026 Commerce Engineering

Shopify Scripts Are Off: A Functions Migration Playbook for Plus (September 2026)

Shopify disabled creating and editing Scripts on April 15, 2026, and Scripts stopped executing entirely on June 30, 2026. Shopify has confirmed the deadline will not move again. If your Plus store still has discount logic, shipping rules, or payment method gating that lived in a Ruby Script, that logic is not running anymore, and your checkout is quietly falling back to Shopify defaults. The replacement, Shopify Functions, is not a port of the same code. Functions are compiled WebAssembly modules that run inside Shopify's own infrastructure, and the migration is an architecture change, not a copy and paste. This playbook covers what actually broke, how Functions differ, and the order to rebuild in so the highest revenue logic comes back first.

Aug 24, 2026 Commerce Engineering

Shopify Checkout Components vs Going Headless: Composable Checkout on Plus (2026)

Shopify's Summer 2026 Edition made Checkout Components generally available on Shopify Plus, and it quietly redraws the build versus buy line for enterprise checkout. For years the choice was binary: accept Shopify's hosted checkout with the customization limits of checkout extensibility, or go fully headless and own the entire storefront including the checkout, taking on the PCI scope, the maintenance, and the risk that comes with it. Checkout Components is the middle path: composable, custom checkout built from Shopify's own primitives, running on Shopify's infrastructure, without going headless. For a large brand that wants a differentiated checkout but does not want to own a payment surface, that is a meaningful new option. This post reads Checkout Components against a full headless build on the axes that actually decide enterprise architecture, PCI scope, upgrade path, performance, and total cost of ownership, and gives a straight framework for choosing.

Ready when you are

Want to discuss this topic?

Start a Conversation