Shopify Scripts to Functions: what breaks and what it costs

Scripts were deprecated on June 30, 2026. Each script type maps to a Function, but not one for one. What carries over, what needs a rethink, and the cost.

Published Updated 8 minute readShopify
Curly braces with an arrow to a hexagon

A thread on the Shopify community forum in June was titled “Shopify Scripts die on June 30”. Most of the replies were Plus stores asking what to replace a tiered discount script with, and the most common answer was an app that did most of it. The date passed. The scripts stopped. The checkouts kept working, without the discounts, and a few stores found out from customers.

Scripts were Ruby programs that ran at checkout on Plus stores. Shopify’s Script Editor page names three kinds: line item scripts that changed prices and granted discounts, shipping scripts that changed shipping methods and discounted rates, and payment scripts that renamed, hid and reordered gateways. Shopify Functions replace all three, and the migration is a rewrite, not a conversion.

TLDR

Every Scripts feature has a Function: the Discount API for line item scripts, Delivery Customization for shipping scripts, Payment Customization for payment scripts. The logic moves across; the code does not. Functions live in a custom app, in Rust or JavaScript, read cart data through a GraphQL query, and return operations instead of mutating the cart. One or two scripts migrate as a feature sprint from $1,500. A dozen interacting scripts is a store build with a discovery phase first.

What each script type becomes

Script type What it did Function API What changes
Line item Discounts, tiered pricing, BOGO, price changes per customer Discount Discounts only. A Function cannot raise a price or rewrite a line item’s properties
Shipping Hide, rename, reorder or reprice shipping rates Delivery Customization Hide, rename and sort. The documentation lists no operation that changes a rate’s price
Payment Hide, rename, reorder payment methods Payment Customization Direct equivalent

The first row hides the most work. Scripts could do anything to a line: change the price up or down, change the quantity, split a line. A discount Function can only reduce what the customer pays. A script that added a surcharge for a payment method, or set a per-customer price above retail, has no direct equivalent and needs a different design, usually a product variant or a fee product.

The second row hides the second most. A shipping script that added $5 to a carrier rate for remote postcodes cannot do that as a delivery customization, because the three operations are hide, rename and sort. The surcharge has to exist as its own rate in the shipping settings or the carrier service; the Function then shows the right one and hides the other.

Why the code does not carry over

Scripts were Ruby, run by Shopify, with the cart as a set of objects you could read and mutate. Functions are compiled to WebAssembly, written in Rust or JavaScript, and they get their input from a GraphQL query you write and return a list of operations. No mutation, no shared state, no external calls, and Shopify recommends Rust for large carts because a Function that runs too long fails.

That last part is the design constraint. A discount script that looped through 200 cart lines and checked each against 50 rules will not survive as a Function unless the rules are precomputed into metafields the query fetches. Most migrations spend more time on where the data lives than on the logic.

A tiered pricing script that gave 10 percent off at 6 units and 20 percent at 12, rewritten as the outline of a discount Function in JavaScript, using the current Discount API’s entry point and operations shape:

export function cartLinesDiscountsGenerateRun(input) {
  const candidates = input.cart.lines
    .filter((line) => line.quantity >= 6)
    .map((line) => ({
      targets: [{ cartLine: { id: line.id } }],
      value: { percentage: { value: line.quantity >= 12 ? "20.0" : "10.0" } },
      message: line.quantity >= 12 ? "20% case discount" : "10% multi-buy",
    }));

  return {
    operations: [{ productDiscountsAdd: { candidates, selectionStrategy: "ALL" } }],
  };
}

Fourteen lines. The script it replaced was forty, because Ruby did the filtering with more ceremony. The input query that feeds it is another ten lines and has to ask for the discount classes the Function is allowed to apply. Then the app that hosts it, the deployment through the Shopify CLI, and the discount created in the admin that points at the Function. A developer who has done one can do the next in a day. The first one takes a week, which is why the migration price is not the line count.

What breaks that nobody expects

Discount combinations. Scripts could apply any number of discounts in any order because the script was the order. Functions produce discounts that combine under Shopify’s discount combination rules, which the merchant sets per discount. A store with a loyalty discount, a case discount and a code for free shipping has to decide which combine with which, and the answer used to be “whatever the script did”.

Customer-specific logic. A script read the customer and its tags freely. A Function reads what the input query asks for, so customer tags, metafields and company are available, but every one of them has to be named in the query. Miss one and the Function runs without it.

Several scripts of one type. Functions can run more than one discount at once, which sounds better until two of them target the same line. The migration usually merges scripts of a type into one Function with the rules in order, because that is what the script was.

Testing. Scripts had a preview in the Script Editor. Functions are tested with the CLI against sample input, and then on a development store with real carts. Stores that skip the second step find the edge cases in production. Every migration should end with a checklist of carts that must produce the same total as before, run on a development store before the switch.

A worked example

Take a supplements store on Plus with four scripts: tiered pricing by quantity, a subscriber discount by tag, free shipping over $75 that hid the paid rates, and a script that put Shop Pay first. The migration is one custom app with three Functions: one discount Function holding both pricing rules with the subscriber discount applied after the tier, one delivery customization that hides paid rates when the free rate is present, one payment customization for the ordering.

The thing to look for in a migration like this is the interaction. If the old subscriber script applied its discount to the already-discounted tier price, a subscriber buying 12 got 20 percent then 10 percent, and the merchant may never have noticed. Writing the rules down before rewriting them is where that surfaces, and the new Function can apply the larger of the two, which the checkout shows clearly. Migrations fix pricing bugs a store has paid for since the script was written.

The migration checklist

For a store doing this with a developer, the order that avoids surprises:

  1. Inventory. One paragraph per script, as above, plus the discount codes and automatic discounts already in the admin, because the Functions have to combine with those.
  2. Data first. Decide where every rule’s inputs live: metafields on products and customers, a metaobject for a price list, or constants in the Function. Anything that needs an external call has to be precomputed.
  3. Build the app. One custom app in the Partner account, with one Function per API type, deployed with the Shopify CLI.
  4. Create the discounts and customizations in the admin that point at the Functions, and set the combination rules.
  5. Test on a development store with the cart matrix below.
  6. Switch on a quiet day, with the old discount codes and the new ones checked side by side on the first ten real orders.

A cart test matrix

The test that catches most migration bugs is a short table of carts and the total each must produce, run before and after. For the supplements store above:

Cart Customer Expected total behavior
6 units of one product Guest 10 percent off, paid shipping
12 units of one product Guest 20 percent off
12 units, subscriber tag Subscriber The larger of 20 percent and the subscriber rate, not both
$80 of mixed products Guest Free shipping shown, paid rates hidden
$70 of mixed products with a free shipping code Guest Code applies, and the Function does not double it
Any cart Any Shop Pay listed first

Six rows is enough for four scripts. A store with a dozen scripts needs thirty, and writing them is where the forgotten rules surface.

Cost, and what to do first

One or two scripts of a single type: a feature sprint from $1,500, one to two weeks. Three or four scripts across types with interactions like the one above: $3,500 to $6,000. A dozen scripts that grew over years, with pricing logic nobody fully remembers, is a store build with a discovery phase first, because the first job is writing down what the scripts did.

Do that part yourself before asking anyone for a quote. Open each script in the Script Editor, which still shows the code, and write one paragraph per script in plain language: who it applied to, what it changed, and what it should not have done. That document is most of the discovery, and a developer quoting from it will give you a number rather than a range.

The B2B pricing guide covers where native catalogs replace a pricing script entirely, and our Shopify development page lists what a migration build includes.

Questions

Our scripts still show in the admin. Are they running?

No. Shopify deprecated Scripts on June 30, 2026 and the Script Editor no longer runs them at checkout. The code is still readable, which is the one useful thing about it while you migrate.

Do Functions need Plus?

In your own app, yes. Shopify's documentation says only Plus stores can use custom apps that contain Functions. Scripts were Plus only too, so a store migrating off Scripts is already on Plus. Functions inside public App Store apps run on any plan.

Have this problem on your store?Two sentences about it get you a written quote, or a plugin or app recommendation, within 48 hours.
Get a fixed quote