Shopify Scripts shut off June 30, 2026. Read the announcement →
Migration Apr 28, 2026 18 min read

The complete Shopify Scripts → Functions playbook for Plus stores

Every Plus-store Script pattern we've migrated, sorted by what your Script actually does. With the gotchas, the edge cases, and the lines you can copy-paste. Read it once before you start — you'll save yourself a deploy or two.

KJ
Krishna J.
Founder · Migrates Scripts daily
BEFORE · SHOPIFY SCRIPT (RUBY)
# Tier discount
if Input.cart.subtotal_price > 100
Input.cart.line_items.each do |item|
  item.change_line_price(
    item.line_price * 0.90,
    message: "10% off $100+"
  )
end
end
Output.cart = Input.cart
AFTER · RULEHOP FUNCTION
// Generated by Rulehop
when cart.subtotal > 100:
apply_to: "all_lines"
type: "percentage"
amount: 10
message: "10% off $100+"

// Tested on draft order ✓
// Live on checkout ✓

If you run a Plus store, you have somewhere between one and forty Shopify Scripts running at checkout right now. Most of them you wrote two years ago. Most of them you’ve forgotten exactly what they do. And on June 30, 2026, every single one of them stops working.

This is the playbook we use internally to migrate them. We’ve grouped patterns by what your Script actually does — not by what its variable names suggest. Every section has the original Script shape, the Function it maps to, and the one or two edge cases that quietly bite.

Why this isn’t a one-line port

Scripts and Functions are different runtimes with different mental models. Scripts mutate Input.cart in Ruby. Functions return a discount declaration in a typed schema. A naïve port — Ruby if-statement to TypeScript if-statement — usually compiles, sometimes runs, and almost always misses one of these:

  • Stacking order. Scripts ran in the order you registered them. Functions are evaluated by Shopify in a fixed precedence — cart > product > shipping — and you can’t change it.
  • Cart rejection. Scripts could throw raise "Cart minimum $20". Functions can’t reject a cart. The closest you get is a discount that pushes the displayed total out of range, and most stores don’t actually want that.
  • Customer tags. Scripts read Input.customer.tags as a comma-separated string. Functions get them as a typed array — but only if the customer is logged in and Shopify has propagated the tag to the checkout context.

The migration is rarely the hard part. The hard part is the 90 seconds you spend reading the diff before you click Deploy. Don’t skip that.

The five patterns that cover 80% of stores

1. Tier discounts (“spend $100, get 10%”)

This is the most common Script we see. The Function form is unambiguous — Rulehop’s migrator gets it right on the first try in >98% of cases.

!
Edge case

If your tier discount excludes sale items via a product tag, the Function needs an explicit where clause — the Script’s next if item.line_price != item.original_line_price won’t translate, because Functions don’t see a “compare-at” price natively.

2. BOGO (“buy 2, get 1 free”)

BOGO is where most agencies’ migrations fall over. The Function rule-tree has to express “find the cheapest matching line item, set that one’s price to zero” — and matching has to be aware of variant SKUs, not only product IDs.

// Rulehop's BOGO rule-tree
match: line_item.product.tag == "summer-tee"
when:  count(matched) >= 2
apply: percentage 100 to cheapest_matched
message: "Buy 2, get 1 free"

3. Customer-tag VIP (“Gold customers get 15% off”)

The cleanest pattern. One tag, one threshold, one Function. We have a separate post on the three tag-format quirks Shopify enforces (coming soon) — the migrator handles them, but it’s worth reading once so you know what to check.

4. Free shipping over $X

This was a Script — now it’s a separate Function (Shipping Discount, not Cart Discount). Same logic, different surface. The migrator splits these out automatically; be aware that “free shipping” rules show up in a different list in your Shopify admin.

5. Cart minimum / rejection

This one doesn’t translate. Functions cannot reject a cart. We have a dedicated post on the three approved workarounds (coming soon) — the cleanest is a Validation Function paired with a discount.

The pre-deploy checklist

Before you click Deploy on any migrated Function, run through this. It takes 90 seconds and catches most of the bugs your customers would otherwise hit at checkout:

  • Test on a draft order at full discount.
  • Test at the threshold ($99.99 vs $100.01).
  • Test with a logged-out customer (tag-based rules silently no-op).
  • Test with a gift-card-only cart (free-shipping rules behave weirdly).
  • Diff the Function against the Script side-by-side. Read it.

That’s it. Rulehop runs the first four automatically before deploy on Pro tier — but the diff read is on you.

If you have one more 10-minute chunk to invest, read the four free-shipping edge cases (coming soon). They bit two Plus stores this month, and the fixes are quick once you know to check.

MigrationPlusFunctionsScripts sunset
Updated · Apr 28, 2026