TLDR: Shopify Scripts stopped executing on June 30, 2026. If your store used Scripts for custom discounts, shipping rules, or payment gating, that logic is gone right now, silently, with no error message. Shopify Functions is the replacement. This guide walks through what broke, how to check if you were affected, and the exact steps to rebuild your checkout logic in Functions, with real code for JavaScript and Rust.
If you are reading this after July 2026 and your Shopify store used to have a wholesale discount, a tiered shipping rule, or a payment method that only showed up for certain customers, and it is not showing up anymore, this is why. Shopify Scripts, the old Ruby based way of customizing checkout, is dead. It did not fail loudly. There was no email that landed in your inbox saying “your discount broke today.” The code just stopped running on July 1, 2026, and your store reverted to default behavior.
I run Shopify and WordPress development for stores in the US, UK, Canada, and Europe, and in the last few weeks I have had clients reach out because a discount or shipping rule quietly stopped applying and they could not figure out why. In every case, the answer was the same. A developer had built the original logic in Scripts years ago, nobody remembered it existed, and it went dark the moment the deprecation deadline passed.
What this guide covers
- The Scripts deprecation timeline, and why it already happened
- How to check if your store is affected right now
- Scripts vs Functions: what actually changed under the hood
- The six Function APIs and which one replaces your old Script
- Auditing and prioritizing your migration
- Building your first Function, JavaScript and Rust code included
- Deploying and testing with Shopify CLI
- Hard limits that will trip you up mid-migration
- What this costs if you hire it out
- FAQ
The Scripts deprecation timeline, and why this already happened
Shopify announced the end of Scripts years ago and pushed the deadline back twice, first from August 2024 to August 2025, then again to June 30, 2026. A lot of merchants and even some agencies treated that pattern as a sign the deadline would move again. It did not. Shopify confirmed publicly that June 30, 2026 was final, and it was.
Two dates mattered, and the first one is the one most people missed:
- April 15, 2026: Editing, updating, or publishing new Scripts stopped being possible. Whatever was live in your Script Editor at that point became frozen code. If a bug showed up between April and June, there was no way to patch it inside the editor.
- June 30, 2026: Every Script still running stopped executing entirely. Not gradually. Not with a warning banner. From July 1 onward, Shopify simply ignores any logic that used to live in Scripts and falls back to standard checkout behavior.
The part that catches store owners off guard is the silence. A broken app throws an error. A broken theme section shows a blank space. A dead Script just does nothing, and nothing looks like nothing. Your checkout still works, your customers can still buy, the only thing missing is the custom logic that used to run in the background, and that is exactly the kind of change that does not get noticed until someone checks their revenue numbers and finds a gap.
How to check if your store is affected right now
Before writing a single line of Function code, confirm whether you actually have anything to migrate. Go to your Shopify admin, open Settings, then look for the Scripts customizations report under your checkout settings, or search Script Editor directly in admin search. This report lists every Script that was active on your store and, in most cases, links to a recommended replacement path.
While you are there, check these three places manually, because the report sometimes misses Scripts that were installed by a developer years ago and never documented:
- Discounts: any wholesale pricing, tiered volume discounts, buy-one-get-one logic, or customer-tag-based pricing that is not showing up as a standard Shopify discount code or automatic discount.
- Shipping: custom shipping rate names, rules that hide certain shipping methods for certain products or destinations, or free shipping thresholds that used to behave differently than the native Shopify setting.
- Payment: payment methods that used to be hidden for specific customer groups, B2B accounts, or order totals, and are now showing to everyone.
The most common miss: B2B and wholesale pricing built years ago by a developer who has since left the project. Nobody on the current team knows it exists as a Script until the wholesale customer emails asking why they were charged retail price.
If you find nothing in any of these three areas, you are one of the lucky merchants who either never used Scripts or already migrated. If you find something, keep reading, because the rebuild is not optional and there is no way to recover the old code from inside the Script Editor anymore.
Scripts vs Functions: what actually changed under the hood
It helps to understand why Shopify did not just patch Scripts and move on. Scripts ran as Ruby code inside a sandboxed environment tied directly to the old checkout.liquid page. That architecture had real limits. It only worked on Shopify Plus. There was no version control, no staging environment, no way to test a change before it went live in production. Every edit went straight into checkout, and every bug went straight to your customers.
Functions solve those problems by being a completely different kind of extension, not a rewrite of the same idea.
| Scripts | Functions |
|---|---|
| Ruby code in the Script Editor | Compiled WebAssembly, written in JavaScript or Rust |
| Shopify Plus only | Available on every Shopify plan |
| Edited live in the admin, no staging | Built and tested locally, deployed as versioned app releases |
| Tied to the legacy checkout.liquid | Built for Checkout Extensibility, works with UI Extensions and Flow |
| No rollback if something breaks | Version pinning, you can revert to a prior release |
| Ran with no hard time limit | 5 millisecond execution cap per invocation |
The version control and rollback difference alone is worth the migration effort. With Scripts, if a discount rule started misbehaving during a big sale, your only option was to delete it and figure out the fix under pressure. With Functions, you deploy a new version, and if something is wrong, you switch back to the previous release from the admin in seconds.
The other change worth knowing about, because it affects timing for some stores: the legacy checkout.liquid page itself is being phased out separately from Scripts, on a different schedule depending on your plan. If your store is still running the old checkout page rather than Checkout Extensibility, that is a second migration sitting on top of this one, and it is worth checking your checkout settings while you are already in there.
The checkout.liquid deadline sitting right behind this one
Scripts and the legacy checkout page were deprecated on related but separate timelines, and mixing the two up is a common source of confusion. Scripts stopped executing for every store on June 30, 2026, regardless of plan. The legacy checkout.liquid page itself follows a plan-based schedule: Shopify Plus stores lost checkout.liquid earlier, and non-Plus stores on the older checkout experience have a later cutoff, in the second half of 2026. If your store has not moved to Checkout Extensibility yet, that migration needs to happen alongside this one, because Functions are built specifically for the extensible checkout and do not attach to the old page.
Practically, this means a store that delayed both migrations is not looking at one project, it is looking at two that need to land in the same window. Check your checkout settings first. If you see options for Checkout UI Extensions or a note about Checkout Extensibility being active, you are on the new system and only need to worry about Functions. If your checkout still looks like the old single-page layout with inline Liquid customization, talk to whoever manages your store about scheduling both migrations together rather than one after the other, since a lot of the audit and testing work overlaps.
The six Function APIs, and which one replaces your old Script
Shopify Scripts came in three flavors: line item scripts (product-level discounts), shipping scripts, and payment scripts. Functions split this into more specific, more capable categories. Since 2025, Shopify unified the old Product Discount, Order Discount, and Shipping Discount APIs into a single Discount Function that can touch cart lines, order totals, and shipping rates from one deployment, which is actually simpler than what you had before.
- Discount Function: replaces line item, order, and shipping discount Scripts. Handles tiered pricing, BOGO, wholesale rules, customer-tag based discounts, and free shipping thresholds.
- Cart Transform: not a direct Script equivalent, but the closest fit if your old Script bundled products, split kits into components, or changed how line items were grouped. One Cart Transform Function per store maximum.
- Delivery Customization: replaces shipping Scripts that hid or renamed shipping methods based on cart contents, destination, or customer type.
- Payment Customization: replaces payment Scripts that hid gateways for certain customers, order values, or product combinations.
- Order Routing: newer than anything Scripts covered, useful if you are also solving fulfillment location logic while you are in here.
- Validation Functions: block checkout under specific conditions, for example minimum order values for B2B accounts. Some merchants used payment Scripts to fake this behavior. Validation is the correct tool now.
Practical mapping: if your old Script started with a check like “if customer has tag wholesale, apply 20 percent off,” that is a Discount Function. If it said “hide local pickup unless the cart total is over $50,” that is Delivery Customization. If it said “hide COD for orders over $500,” that is Payment Customization.
Auditing and prioritizing your migration
Do not start writing Functions before you have a full list of what needs to be rebuilt. The stores that get this wrong usually rebuild the Script they remember and forget the one nobody documented. Work through this order:
- Pull the customizations report from your Shopify admin. This is your starting inventory, not your final one.
- Interview whoever ran the store before you, or check old developer handoff notes, Slack threads, or emails, for any custom checkout behavior that might not show up in the report.
- Categorize by revenue impact. A wholesale pricing rule affecting a handful of high-value B2B accounts is a higher priority than a minor shipping label rename.
- Categorize by complexity. Simple percentage discounts can often be replaced with a public app in a day. Custom pricing matrices, stacking rules, or anything that pulled data from outside Shopify needs a custom Function build.
- Build a test plan for each one. Before you consider a migration done, run the exact scenario the old Script handled and confirm the new Function produces the same result.
For a mid complexity store with five to fifteen Scripts to replace, budget real time for this. Scoping, building, testing on a development store, and a monitored production rollout is not a weekend project even for a small catalog.
Building your first Function: a volume discount example
The fastest way to understand Functions is to build one. Here is a simplified Discount Function that replaces a common Script pattern: buy three or more of a product, get 15 percent off that line. This is JavaScript, which is the right starting point unless you already know you need Rust.
Start by scaffolding the extension with the Shopify CLI inside an existing app:
shopify app generate extension
# choose: Function
# choose: Discount (Cart lines and order discounts)
# choose: JavaScript
This generates a folder with a config file and a starter run function. The core logic lives in the run function, and it looks roughly like this:
// cart_lines_discounts_generate_run.js
export function cartLinesDiscountsGenerateRun(input) {
const operations = [];
const candidates = [];
for (const line of input.cart.lines) {
if (line.quantity >= 3) {
candidates.push({
message: "Buy 3+, save 15%",
targets: [
{
cartLine: {
id: line.id
}
}
],
value: {
percentage: {
value: 15
}
}
});
}
}
if (candidates.length > 0) {
operations.push({
productDiscountsAdd: {
candidates,
selectionStrategy: "ALL"
}
});
}
return { operations };
}
A few things worth pointing out if you are coming from Scripts. There is no direct access to the customer’s browser session, no way to write to a database mid-request, and no external API calls during execution. The function receives a snapshot of the cart as input, and it returns a list of operations describing what should change. Shopify applies those operations. That is the entire contract.
If your old Script needed data from outside Shopify, like a loyalty tier stored in a third-party system, you cannot call that system live from inside a Function. You need to sync that data into a Shopify metafield ahead of time, on the customer or product, and have your Function read the metafield as part of its input query instead.
Rust version for the same logic: if you are hitting the instruction limit on large carts, or your team already writes Rust, the same discount logic compiles down to a smaller, faster Wasm binary. Most agencies, including my own workflow, default to JavaScript and only move to Rust for Cart Transform or Validation Functions doing heavy computation over large carts.
A second example: hiding a shipping method with Delivery Customization
A common Script pattern was hiding local pickup or a specific carrier unless the cart met a condition, for example a minimum order value or a specific product tag. Here is the same logic as a Delivery Customization Function. The shape is different from a discount Function because instead of adding a discount, you are returning an operation that hides a delivery option:
// cart_delivery_options_transform_run.js
export function cartDeliveryOptionsTransformRun(input) {
const operations = [];
const cartTotal = parseFloat(input.cart.cost.subtotalAmount.amount);
for (const option of input.cart.deliveryGroups[0].deliveryOptions) {
const isLocalPickup = option.title.toLowerCase().includes("pickup");
if (isLocalPickup && cartTotal < 50) {
operations.push({
hide: {
deliveryOptionHandle: option.handle
}
});
}
}
return { operations };
}
Notice the pattern is identical in spirit to the discount example: read the input, check a condition, push an operation, return the list. Once you have built one Function, the rest of the migration is mostly repeating this shape against different input shapes and different operation types. The GraphQL input query at the top of each generated file is what changes the most between Function types, since it defines exactly what data your logic is allowed to see.
No-code path: when you do not need to write a Function at all
Not every old Script needs a custom rebuild. If your Script was doing something common, like a straightforward tiered volume discount, a buy-X-get-Y offer, or a simple free shipping threshold, there is a good chance a Function-based app from the Shopify App Store already covers it. These apps ship pre-built Functions that merchants configure through a normal admin interface, no code required.
The tradeoff is control. A public app gives you a fixed set of configuration options built for the average merchant. If your old Script had a slightly unusual condition, stacking two discount types together in a specific order, or checking a metafield value that no app anticipated, you will hit a wall with the no-code option and end up needing a custom Function anyway. My rule of thumb when auditing a store: if the logic can be described in one plain sentence with no “and if” clauses, try an app first. If describing it needs multiple conditions chained together, plan for custom development from the start rather than losing a week trying to force it into an app’s settings panel.
Common mistakes I see during this migration
A few patterns show up repeatedly across the migrations I have worked on, and most of them are avoidable with a bit of planning before the first line of code gets written.
- Migrating the Script you remember and missing the one you do not. Old B2B pricing rules and legacy shipping exceptions are the most commonly forgotten, because they were often set up once for a single large client and never touched again.
- Assuming stacking behavior carries over exactly. Scripts allowed fairly loose stacking of multiple discounts on the same line item. Functions default to more conservative behavior, generally one discount per line unless your logic explicitly handles combining them. Test stacked discount scenarios specifically, do not assume they behave the same.
- Skipping the empty cart and edge case tests. Functions must handle an empty cart, a guest checkout with no customer object, and products with no metafields gracefully. Scripts often failed silently in these situations. Functions surface these as hard errors if you have not accounted for them, which is stricter but ultimately safer.
- Forgetting to activate the Function after deployment. Deploying a Discount Function does not create a discount. You still need to create the discount in the admin or via the Admin API and point it at the Function, or nothing changes at checkout.
- Testing with tiny sample carts only. A Function that works fine with three line items can behave very differently with fifty. If your store supports bulk or wholesale ordering, test with realistic cart sizes before going live, not after.
Deploying and testing with the Shopify CLI
Once your function logic is written, the workflow to get it live looks like this:
# Run it locally against a sample cart before touching production
shopify app function run
# Push to a development store for real end-to-end testing
shopify app dev
# Debug a specific execution that happened on the dev store
shopify app function replay
# When you're confident, create and release a version
shopify app deploy
After deploying, the Function does not activate itself. For a Discount Function, you still need to create the actual discount in your Shopify admin, or via the Admin GraphQL API, and point it at your deployed Function. This is the step people forget, then wonder why nothing changed at checkout after a successful deploy.
Phased rollout tip: Scripts and Functions were able to run side by side while merchants migrated. Build the Function, test it against a small segment using a customer tag or a preview link, confirm it matches the old Script’s behavior exactly, and only then remove the legacy logic. Since Scripts stopped executing on June 30, this only applies if you are still catching up on the migration now, but it is the safest pattern if you are mid-project.
Hard limits that will trip you up mid-migration
Functions are faster and more reliable than Scripts, but they run inside a genuinely constrained sandbox. These are the limits that catch teams off guard partway through a build, after they have already assumed the old Script logic would translate cleanly:
- Hard execution time cap per invocation. Go over it, and Shopify aborts the Function and ignores its output. Complex logic with heavy iteration over 50-plus line item carts needs to be profiled, not assumed safe.
- An instruction count ceiling. For a simple discount or shipping rule, you will never come close. For B2B pricing matrices with many tiers and many line items, this is a real constraint.
- A compiled binary size limit. Mostly a Rust concern, since JavaScript compiles through a toolchain that manages this automatically.
- No network access at runtime. Anything your old Script fetched from an external API has to be pre-synced into Shopify metafields. There is no workaround for this one.
- No randomness, no clock access. Functions must be deterministic. A Script that picked a random free gift or checked the current time of day needs to be redesigned around this constraint.
- Per-store limits on how many Functions of each type you can run. If you had a dozen separate Scripts, some of that logic needs to be consolidated into fewer, smarter Functions rather than migrated one-to-one.
That last point matters more than it sounds. A lot of the Script sprawl I see in older stores is a result of adding a new Script every time a new discount rule was needed, rather than extending an existing one. Functions force more discipline here, which is a good thing for long-term maintainability, but it means your migration is a redesign, not a copy-paste.
Where this connects to the rest of your store’s performance
Functions run server-side and add close to nothing to page load, so this migration will not hurt your Core Web Vitals scores the way a bloated app might. But if you are already in your theme code dealing with checkout logic, it is worth checking whether your product schema markup is still accurate after any Cart Transform changes, since bundled or split line items can affect how pricing shows up in structured data. And if part of your original Script logic was built to boost conversion, like a threshold-based free shipping nudge, this is also a good moment to revisit your conversion rate optimization setup while the checkout code is already open.
Scripts died silently. If you’re not sure what your store lost, or you don’t have the time to rebuild wholesale pricing, shipping rules, or payment logic from scratch, I handle these migrations end to end: audit, build, testing, deployment.
Book a free Scripts audit callMonitoring after you go live
The migration is not finished the moment your Functions are deployed and the discounts start showing up correctly in testing. Scripts had a habit of running for years without anyone checking on them, and that same habit is what caused this whole situation in the first place. Do not repeat it with Functions.
Once your replacements are live, check the Functions logs in your Partner Dashboard weekly for the first month, then monthly after that. Every invocation is logged with its input, output, and any errors, which makes it far easier to catch a misfiring discount early than it ever was with Scripts. Watch specifically for two things: Functions that are silently returning no operations when they should be returning one, which usually points to an input query that is not fetching the field your logic depends on, and Functions that are getting close to the execution time limit, which tends to happen gradually as a store’s catalog or average cart size grows.
It is also worth setting a calendar reminder tied to Shopify’s quarterly API version releases. Functions are bound to a specific API version, and while Shopify gives a generous window before an old version stops being supported, letting several versions pass unmigrated turns a small quarterly update into a larger one later. Treat version updates the same way you would treat a theme or app update: small and frequent beats large and overdue.
What this costs if you hire it out
Pricing depends entirely on what your old Scripts were doing, so treat these as ranges, not quotes:
- Simple percentage or quantity discounts that a public app can now handle natively: often a same-week fix, sometimes no custom code at all.
- Standard shipping or payment customization, one or two rules, no external data dependencies: a short, well-scoped custom Function build.
- Complex logic, B2B pricing matrices, stacking discount rules, custom shipping by zone or tag, payment gating tied to multiple conditions: this is where most of the real engineering time goes, and where hiring an experienced Shopify developer pays for itself, because a Discount Function deployed carelessly can misfire during your next sale.
For Shopify Plus merchants already paying a premium for the platform, the cost of getting this rebuilt properly is small compared to what a broken wholesale discount or a hidden payment method costs in lost or duplicated orders over even a single week.
Do not wait for a customer complaint to find out what broke. The stores I have seen affected did not notice through analytics. They noticed because a wholesale customer emailed asking why they were charged full price, weeks after the fact.
FAQ
Can I still use Shopify Scripts if I stay on the legacy checkout?
No. Both checkout.liquid and Scripts were deprecated together. Even stores still technically on the legacy checkout page lost their Script functionality on June 30, 2026.
Is there an automated tool that converts my old Ruby Scripts into Functions?
No. Ruby and the WebAssembly runtime Functions use are fundamentally different, so there is no one-to-one converter. Every Script needs to be manually reviewed and rebuilt as a Function, ideally by someone who understands both the old logic and the new API constraints.
Do I need Shopify Plus to use Functions?
No, and this is one of the real upgrades over Scripts. Any store can install Functions-based public apps from the Shopify App Store. Building a fully custom Function app for bespoke logic that is not covered by an existing app does require Shopify Plus.
My discount just disappeared. Is there any way to get the old Script code back?
If you have access to your Script Editor history, code you last edited before April 15, 2026 may still be visible even though it no longer runs, which is worth checking before you rebuild from memory. If not, and nobody kept a copy, you will need to reconstruct the logic from documentation, old developer notes, or by testing what the discount used to do.
How long does a typical migration take?
For five to fifteen Scripts of moderate complexity, plan for several weeks including audit, build, testing on a development store, and a monitored production cutover. Larger catalogs with many promotional rules should be broken into waves rather than migrated all at once.
The bottom line
Shopify Scripts are gone, and the deadline is not coming, it already passed. If your checkout logic went quiet on July 1 and you have not rebuilt it yet, every day that passes is a day of lost discounts, wrong shipping rates, or payment methods showing to customers who should not see them. The audit takes an afternoon. The rebuild is the part that needs real development time, and it is worth doing properly the first time rather than patching it twice.
Not sure what your store lost, or need someone to rebuild it correctly the first time? Let’s audit your checkout together.
Free consultation with Hamza TajNeed this fixed on your store rather than doing it yourself? I’ve spent years building Shopify and WordPress solutions for stores across the US, UK, Canada, and Europe. See my Shopify project portfolio, browse WordPress projects, or check my full portfolio. Read more Shopify and WordPress guides on the blog, learn more about me, or get in touch directly.