Shopify Development

Shopify Scripts Sunsets on 30 June: Your Functions Migration Checklist

12 min read

On 30 June, Shopify Scripts stop running. Every line-item discount, shipping rule and payment gateway tweak still living in the Script Editor will cease to execute on that date — not deprecated, not read-only, switched off. If your Plus store relies on Scripts for pricing logic, you have 27 days to replace it.

We understand the scepticism. This deadline has slipped before, and plenty of merchants quietly concluded it would keep slipping. Everything Shopify has done since says otherwise: checkout.liquid is already gone, the Script Editor stopped accepting new installs long ago, and Shopify Functions has had years of investment as the designated replacement. The runway ends this month.

This is the migration guide for the brand that has been putting it off. We have moved several Script-heavy Plus stores onto Functions over the past two years, and the work is more mechanical than you fear, provided the audit starts this week rather than next.

30 Jun

the hard sunset date for all Shopify Scripts, with no further extension announced

3

script types to migrate: line-item discounts, shipping rates, payment methods

0

Ruby scripts that will execute at checkout on 1 July, whatever state your migration is in


Who is affected, and what breaks on the day

Scripts were always a Plus-only feature, so if you are on a standard Shopify plan you can stop reading, though if the sunset has you re-evaluating whether Plus still earns its fee, that is a separate and worthwhile conversation. For Plus merchants, three families of logic are on the chopping block.

Line-item scripts are the big one: tiered pricing, customer-tag discounts for VIP and wholesale segments, automatic gift-with-purchase, bundle maths. Shipping scripts hide, rename, reorder or discount delivery rates based on cart contents or destination. Payment scripts gate payment methods: hiding cash on delivery above a threshold, or surfacing invoice payment only for tagged B2B customers.

On 1 July, none of it runs. Carts price at the base price, every shipping rate displays raw, and every payment method shows for every customer. Your VIP tier pays full price and your B2B accounts see a credit card form.

Nothing errors and nothing alerts you; checkout simply behaves as if the logic never existed. That failure mode is margin leak and conversion damage at the same time, and the brands it hurts most are the ones who find out from a confused customer email on the second of July.

"The Ruby sitting in your Script Editor is probably the only written specification of your pricing logic that exists. Export it before you lose access to it."


Audit your Scripts inventory this week

Do not start by writing Functions code. Start by establishing exactly what you are replacing, because in the audits we run, somewhere between a quarter and a third of Script logic turns out to be dead weight: promotions with hardcoded 2023 dates, half of an A/B test nobody ended, a wholesale rule for a channel the brand exited. Migrating dead logic wastes the one thing you do not have, which is time.

The audit is a few hours of focused work:

  • Export every script's source into version control. Copy the Ruby out of all three Script Editor tabs today. Do not assume the editor stays accessible after the sunset.

  • Document each rule in plain English. Trigger conditions (tags, thresholds, products), the maths, and the edge cases: zero-quantity lines, mixed-currency carts, subscription items.

  • Mark what is genuinely live. Check order data for the past 90 days: which rules actually fired? Anything that never triggers gets deleted, not migrated.

  • Map external dependencies. Discount codes that interact with script output, customer tags written by other systems, selling plans whose pricing the scripts adjust.

  • Assign an owner per rule. Someone has to confirm intended behavior when the port inevitably surfaces an ambiguity, because the Ruby is the only spec most of these rules ever had.


Mapping Scripts concepts to Shopify Functions

Functions cover the same three surfaces Scripts did, but the boundaries sit in different places. The mapping below is where every migration plan should begin.

Scripts → Functions migration map

Line-item scripts

Tiered pricing, tag-based discounts, gift-with-purchase, bundle maths

Ruby · Script Editor

Discount API

One Function API spanning product, order and shipping discount classes. Cart lines, customer tags and attributes all available in the input query

Wasm · app extension

Shipping scripts

Hiding, renaming, reordering and discounting delivery rates

Ruby · Script Editor

Delivery Customization + shipping discounts

Hide, rename and reorder via Delivery Customization. Price changes move to the Discount API's shipping class; one script often becomes two functions

Wasm · app extension

Payment scripts

Gating, reordering and renaming payment methods by customer or cart

Ruby · Script Editor

Payment Customization

Hide, move and rename payment methods. The closest one-to-one port of the three: most payment scripts translate almost line for line

Wasm · app extension

The three Script types and their Functions counterparts. Note the split in the middle row: shipping scripts that change prices migrate to a different API than shipping scripts that hide or rename rates.

That middle row is the mapping mistake we see most often. Delivery Customization cannot change a rate's price; it only hides, renames and reorders. A shipping script that knocked £3 off standard delivery for subscribers needs a shipping discount through the Discount API instead, so one Ruby file frequently becomes two functions with different deployment targets.

All of these live inside an app rather than the admin. If you already went through checkout extensibility, the app you built for your checkout UI extensions is the natural home for your functions too: one codebase, one deploy pipeline, one place to look when checkout behaves oddly.


What actually changes for your team

The technical port is the smaller half of this migration. The bigger shift is operational: Scripts were edited inline in the admin and published instantly; Functions are compiled code, deployed through a CLI, living in a repository.

Before · Script Editor

  • Ruby edited in an admin text box
  • Publish takes effect immediately
  • No version control, no code review, no tests
  • One published script per type, per store
  • Anyone with admin access can change pricing logic

After · Shopify Functions

  • Rust or JavaScript compiled to WebAssembly
  • Deployed as an app extension via Shopify CLI
  • Git history, pull requests, unit tests and CI
  • Multiple functions per surface, with combination rules
  • Runs in milliseconds at checkout scale

The operational trade: you lose instant inline edits and gain an actual software development lifecycle around your pricing logic.

The loss of quick edits is real and you should plan for it. A merchandiser can no longer nudge a free-shipping threshold mid-promotion from the admin. The fix is to design for it: build your functions to read their configuration from metafields, so the code defines the rule and the merchant edits the values. Thresholds, percentages and tag lists become settings rather than source code, and most day-to-day changes stop requiring a deploy at all.

In return you get things Scripts never offered. Pricing logic with a git history. The ability to unit test a discount rule against forty cart fixtures before it touches production. A review step between "intern edits Ruby" and "checkout changes for every customer". For any brand where Scripts had become load-bearing revenue infrastructure, this is the upgrade those scripts always deserved.

You are trading convenience for engineering discipline, and on balance the trade is a good one.


What has no clean Functions equivalent

Most Scripts port cleanly. A few patterns do not, and it is better to know which before week two of three.

Scripts changed the price itself; Functions apply discounts. A line-item script that set a bundle component to £0 simply showed £0. The Functions equivalent produces a discount allocation, which renders as a struck-through price and a discount line in checkout and in the order admin. The totals match but the presentation differs, and your customer service team will field questions if nobody warns them. Where the bundle presentation genuinely matters, look at the Cart Transform API, which can merge components into a single bundle line — a different tool, but often the better model for what the script was faking.

The single mega-script. Plenty of stores have one sprawling Ruby file where discount, shipping and payment concerns share helper methods and mutable state. Functions force a separation: one function per surface, each with its own input query. Shared logic either gets duplicated or extracted into a module both functions import. Budget time for the untangling, not just the translation.

Genuinely weird one-offs. Every long-lived Script Editor contains at least one hack — a hardcoded SKU exception from a 2022 collab, a rule that only made sense alongside a theme feature that no longer exists. The honest workaround for most of these is deletion. Functions also enforce input size and instruction limits; across the migrations we have run that has bitten exactly once, on a wholesale cart with hundreds of lines and a quadratic loop, and the fix was leaner logic rather than a different platform.


The three-week plan

Twenty-seven days is enough for a typical Plus store with a handful of live scripts, and it even leaves a buffer. Here is how we would structure it, starting Monday.

Week 1 · to 14 Jun

Audit and scaffold

Run the inventory audit, delete dead rules, export all Ruby to your repo. Scaffold the app with Shopify CLI and port your simplest script first to prove the pipeline end to end.

Week 2 · to 21 Jun

Build and parity-test

Port the remaining functions and wire metafield configuration. Replay representative carts on a development store and diff every total, to the penny, against what production scripts produce.

Week 3 · to 29 Jun

Staged cutover

Cut over one surface at a time in a low-traffic window: payment first, then delivery, discounts last. Unpublish each script as its function goes live, and watch orders for 48 hours per surface.

A 27-day runway split into three working weeks, with discounts — the highest revenue risk — cut over last and monitored hardest.

A word on the parity testing itself: it only works if the fixture set is honest. Pull twenty to forty real carts from recent order history rather than inventing tidy examples — include the subscriber with stacked tags, the wholesale order with ninety lines, the gift-with-purchase cart that also carries a discount code. Diff subtotals, shipping and tax against the matching production order, and record every mismatch with a reason. Most differences turn out to be presentation rather than money, but you want that established in a spreadsheet before cutover, not argued about in a support thread afterwards.

Two rules make the cutover safe. First, never run a script and its replacement function on the same surface at the same time; an automatic discount stacking on top of a still-published line-item script will double-apply, and you will give away margin in the exact week you were trying to protect it. Unpublish the script, activate the function, verify, move on.

Second, retire your scripts yourself rather than letting the platform do it. A controlled cutover on 24 June with someone watching the order feed beats an uncontrolled one at midnight on the 30th with nobody watching anything.

On language choice: pick JavaScript unless you already have Rust in-house. Performance is rarely the constraint for typical cart logic, and your team's ability to maintain the code in eighteen months matters more than shaving microseconds now.


Gotchas from real migrations

These are the three issues that have actually cost time on migrations we have been close to: none fatal, all annoying, all cheaper to know about in advance.

Gotcha #1

Currency rounding

Scripts worked in integer cents; Functions deal in decimal amounts, and multi-currency presentment rounds at different points in the calculation. On one migration we were involved in, EUR carts came out a penny different from the old script's totals — harmless, but it tripped the finance team's reconciliation checks for a day. Diff your parity tests per currency, agree a tolerance, and tell finance before cutover rather than after.

Gotcha #2

Combined discounts

A script could quietly coexist with discount codes because it rewrote prices upstream of them. Functions discounts participate in Shopify's combination rules, and combining is off by default. If customers could previously stack a welcome code on top of script-driven VIP pricing, that stops working the moment you cut over, unless you set combinesWith deliberately. Decide which combinations you actually want; this is also a rare chance to close stacking loopholes you never intended.

Gotcha #3

Draft order behavior

Script pricing applied when a customer paid a draft order invoice through checkout. Functions-driven discounts have not behaved identically on draft orders in our testing, and the behavior has shifted across API versions. If your wholesale or support team leans on draft orders, run that exact flow on a development store before cutover; do not assume parity on the one path your parity tests probably skipped.


Treat the deadline as a forcing function

There are two ways to spend the next three weeks. One is a grudging like-for-like port that gets you to 1 July with identical behavior and nothing learned. The other treats the sunset as the excuse to do what should have happened years ago: delete the dead rules, write down the live ones, and put your pricing logic somewhere it can be tested, reviewed and rolled back.

The second path costs perhaps two extra days and pays for itself the first time a promotion goes wrong and you can see exactly what changed, when, and who approved it. It also leaves you positioned for the rest of the Functions catalog — cart validation, cart transforms, fulfillment constraints — none of which Scripts could ever touch. We have mapped where each of these sits in the wider stack in our guide to checkout components.

Twenty-seven days is enough. It will not be enough in two weeks.

If you are staring at a Script Editor full of undocumented Ruby and a calendar that suddenly looks short, start with the audit. And if you want a second pair of eyes on what you find, that is exactly the kind of thing we look at in an audit of our own.