<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Alex Massaad</title>
    <description>The latest articles on DEV Community by Alex Massaad (@alex_massaad_2ef60150e5ae).</description>
    <link>https://dev.to/alex_massaad_2ef60150e5ae</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3944776%2F1ffe6bed-12f7-4470-9dc2-75ce5029c00a.jpg</url>
      <title>DEV Community: Alex Massaad</title>
      <link>https://dev.to/alex_massaad_2ef60150e5ae</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alex_massaad_2ef60150e5ae"/>
    <language>en</language>
    <item>
      <title>Shopify Functions vs Shopify Scripts: A Migration Walkthrough</title>
      <dc:creator>Alex Massaad</dc:creator>
      <pubDate>Thu, 21 May 2026 19:52:16 +0000</pubDate>
      <link>https://dev.to/alex_massaad_2ef60150e5ae/shopify-functions-vs-shopify-scripts-a-migration-walkthrough-2fj4</link>
      <guid>https://dev.to/alex_massaad_2ef60150e5ae/shopify-functions-vs-shopify-scripts-a-migration-walkthrough-2fj4</guid>
      <description>&lt;p&gt;We covered the Scripts deprecation in an &lt;a href="https://victoriagarland.ca/blog/shopify-scripts-deprecation-june-2026/" rel="noopener noreferrer"&gt;earlier post&lt;/a&gt;. The short version: Shopify Scripts stop running on June 30, 2026, and Shopify Functions are the replacement. As of April 15, 2026, you can no longer edit or publish new Scripts, which means Plus merchants who haven't migrated yet are now in the danger zone.&lt;/p&gt;

&lt;p&gt;This post is the next layer down. It's the walkthrough. What the actual migration looks like in code, what breaks in translation, and the order we run the work in when a client hands us a Script Editor full of Ruby and asks us to make it Functions.&lt;/p&gt;

&lt;p&gt;If you have a developer on your team, this gives them a sane starting point. If you don't, it gives you a clear picture of what you're hiring for.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Mental Model Shift
&lt;/h2&gt;

&lt;p&gt;Scripts and Functions solve the same problem in very different ways. The biggest mistake we see is people trying to port Scripts line-by-line. That doesn't work, because the underlying contract is different.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scripts:&lt;/strong&gt; Ruby code that runs inside Shopify's checkout, mutates a &lt;code&gt;Cart&lt;/code&gt; object directly, and returns the mutated cart. You read properties, you write properties, you're done.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Functions:&lt;/strong&gt; A WebAssembly module that receives a GraphQL input, returns a list of &lt;em&gt;operations&lt;/em&gt; (FunctionRunResult), and lets Shopify apply those operations. You don't mutate state. You declare what should change.&lt;/p&gt;

&lt;p&gt;If you internalize that one shift, the rest of the migration is mechanical.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Migration Order We Run
&lt;/h2&gt;

&lt;p&gt;We don't write a single line of Function code until steps 1 through 3 are done. Skipping ahead is how migrations slip three weeks past the deadline.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Audit the Scripts.&lt;/strong&gt; Read every active Script in Script Editor. Write down what each one does in plain English, including edge cases. Half the time, a Script does something the merchant forgot was even live.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Map to native features.&lt;/strong&gt; Shopify's native discount and checkout customization features have grown a lot since 2016. Several Scripts we audit each year don't need to become Functions. They can become native Automatic Discounts or Discount Combinations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Group what's left.&lt;/strong&gt; The leftover Scripts get grouped by extension point. Discount logic goes to a Discount Function. Shipping logic goes to a Delivery Customization Function. Payment logic goes to a Payment Customization Function.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pick the toolchain.&lt;/strong&gt; We build Functions on Gadget.dev because it cuts the boilerplate dramatically, but vanilla Shopify CLI plus a Rust or JS template works fine for a one-off.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Translate, deploy, test in a development store, then promote.&lt;/strong&gt; Never test Function changes against the live checkout. Use a dev store with the same product catalog shape.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  A Real Translation: Tiered Discount
&lt;/h2&gt;

&lt;p&gt;Here's the canonical "buy more, save more" Script that almost every Plus merchant has some version of.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before (Ruby Script):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TieredDiscount&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;line_items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;quantity&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;discount_percent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt;
      &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
      &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
      &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;
      &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;discount_percent&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="n"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;line_items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;line_item&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;line_item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;change_line_price&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;line_item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;line_price&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Decimal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;discount_percent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="ss"&gt;message: &lt;/span&gt;&lt;span class="s2"&gt;"Tier discount (&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;discount_percent&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;% off)"&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="no"&gt;TieredDiscount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="no"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cart&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After (Shopify Function in JavaScript):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/run.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;totalQuantity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;discountPercent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nx"&gt;totalQuantity&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nx"&gt;totalQuantity&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;  &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nx"&gt;totalQuantity&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;  &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;discountPercent&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;discounts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="na"&gt;discountApplicationStrategy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;FIRST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;discounts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Tier discount (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;discountPercent&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;% off)`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;targets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;productVariant&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;merchandise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;quantity&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;})),&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;percentage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;discountPercent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;discountApplicationStrategy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;FIRST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few things to notice in the diff:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No mutation.&lt;/strong&gt; We return a &lt;code&gt;discounts&lt;/code&gt; array, we don't reach into the cart and rewrite line prices.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Targets are explicit.&lt;/strong&gt; Scripts let you call &lt;code&gt;change_line_price&lt;/code&gt; on each line. Functions ask you to declare which variants the discount applies to and how it stacks via &lt;code&gt;discountApplicationStrategy&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The data shape is GraphQL.&lt;/strong&gt; &lt;code&gt;cart.line_items&lt;/code&gt; is now &lt;code&gt;cart.lines&lt;/code&gt;, &lt;code&gt;line_item.quantity&lt;/code&gt; is now &lt;code&gt;line.quantity&lt;/code&gt;, and you'll spend the first hour of every migration relearning these names.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The GraphQL Input File Is Where People Get Stuck
&lt;/h2&gt;

&lt;p&gt;Functions don't receive the entire cart. They receive whatever you ask for in an &lt;code&gt;input.graphql&lt;/code&gt; query. This is the file that surprises every developer the first time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="c"&gt;# src/input.graphql&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="k"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;cart&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="n"&gt;merchandise&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ProductVariant&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you forget to query &lt;code&gt;tags&lt;/code&gt; here, you cannot read tags inside &lt;code&gt;run.js&lt;/code&gt;, no matter how many &lt;code&gt;console.log&lt;/code&gt; calls you add. The first hour of debugging a misbehaving Function is almost always "the input query is missing a field."&lt;/p&gt;

&lt;h2&gt;
  
  
  Shipping and Payment Customization
&lt;/h2&gt;

&lt;p&gt;Scripts had a single execution point: checkout. Functions split that into separate APIs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Delivery Customization Function&lt;/strong&gt; for shipping. Hide options, rename them, reorder them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payment Customization Function&lt;/strong&gt; for payment methods. Same operations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cart Transform Function&lt;/strong&gt; if you need to expand a single line into multiple lines (bundles, components).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A Script that hid Express Shipping for heavy carts becomes a Delivery Customization Function that returns a &lt;code&gt;hide&lt;/code&gt; operation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;totalGrams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;merchandise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;weight&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;line&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;totalGrams&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;20000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;operations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;expressOption&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deliveryGroups&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flatMap&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;group&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deliveryOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;option&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;option&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Express&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;expressOption&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;operations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;operations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;hide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;deliveryOptionHandle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;expressOption&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handle&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The pattern is consistent across all three customization APIs: query what you need, decide what should change, return operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gotchas We Hit Often
&lt;/h2&gt;

&lt;p&gt;These are the ones that have actually cost us hours, not the ones we read about.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The instruction-count limit is real.&lt;/strong&gt; Functions enforce a strict execution budget measured in instructions (around 11 million), not wall-clock time, along with a 125KB input size cap. Loops over the entire catalog, regex on every line item description, and deeply nested &lt;code&gt;.find()&lt;/code&gt; chains will blow past it on a busy cart. Profile early.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;undefined&lt;/code&gt; vs missing fields.&lt;/strong&gt; GraphQL only returns what you queried. If a field isn't in &lt;code&gt;input.graphql&lt;/code&gt;, it's not &lt;code&gt;null&lt;/code&gt; in your input object, it's not present at all. Defensive code matters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No external HTTP calls.&lt;/strong&gt; Scripts didn't allow them either, but people sometimes assume Functions do because Functions are deployed as part of an app. They don't. If you need data from outside Shopify, fetch it ahead of time and pass it through metafields.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Discount stacking.&lt;/strong&gt; Scripts let you stack discounts implicitly. Functions force you to declare a &lt;code&gt;discountApplicationStrategy&lt;/code&gt; of &lt;code&gt;FIRST&lt;/code&gt; or &lt;code&gt;MAXIMUM&lt;/code&gt;. Pick the wrong one and your customers see different totals than they did under Scripts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Localization and currency.&lt;/strong&gt; Scripts gave you the cart's currency on the cart object. In Functions, you query it via &lt;code&gt;cart.cost.totalAmount.currencyCode&lt;/code&gt; (or similar paths depending on the API). Hardcoding CAD because the dev store is in CAD is the kind of bug that ships to a US store and breaks Black Friday.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Testing tools are different.&lt;/strong&gt; Scripts had a console in the admin. Functions are tested with &lt;code&gt;shopify app function run&lt;/code&gt; against fixture inputs. Build out a small set of fixture carts that represent your real edge cases, and run them on every change.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Long Does a Migration Take?
&lt;/h2&gt;

&lt;p&gt;For a single straightforward Script (one rule, one extension point), expect a day of work end to end. Audit, translate, test, deploy.&lt;/p&gt;

&lt;p&gt;For a Plus merchant with eight to twelve active Scripts spread across discounts, shipping, and payment, expect two to three weeks. Most of that time is in steps 1, 2, and 5. The actual coding is the smallest part.&lt;/p&gt;

&lt;p&gt;If your store has Scripts you've forgotten about, has Scripts written by someone who left the company, or has Scripts that interact with each other in ways nobody documented, double the estimate. We've seen migrations that took six weeks because the audit kept turning up new edge cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  How We Run Scripts to Functions Migrations
&lt;/h2&gt;

&lt;p&gt;When clients bring us this work, our process is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Read-only audit week.&lt;/strong&gt; We get Script Editor access, document everything, and produce a migration plan with native-feature swaps called out.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build week(s).&lt;/strong&gt; Functions written, deployed to a dev store, tested against fixture carts that match the merchant's real traffic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cutover.&lt;/strong&gt; Functions enabled in production, Scripts disabled the same day. We watch checkout closely for 48 hours.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Post-cutover review.&lt;/strong&gt; We document what changed and hand the codebase off, since Functions live in a real Git repo, not in an admin panel.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you're sitting on Scripts and the June 30 deadline is starting to feel close, &lt;a href="https://victoriagarland.ca/contact/" rel="noopener noreferrer"&gt;get in touch&lt;/a&gt;. We can run the audit week and tell you exactly how big the project is before you commit to a build. For more on how we build Functions and apps quickly, see &lt;a href="https://victoriagarland.ca/blog/why-we-build-shopify-apps-on-gadget-dev/" rel="noopener noreferrer"&gt;why we build Shopify apps on Gadget.dev&lt;/a&gt; and our &lt;a href="https://victoriagarland.ca/services/shopify-app-development/" rel="noopener noreferrer"&gt;Shopify app development service&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bottom Line
&lt;/h2&gt;

&lt;p&gt;The migration isn't conceptually hard. It's a translation job between two systems that solve the same problem. The discipline is in the audit and the testing, not the coding. Start with what you have, map what's native, and write Functions only for what's actually left.&lt;/p&gt;

&lt;p&gt;The merchants who started this work in 2025 are done. The ones who started in early 2026 are mid-project. The ones who haven't started yet still have time, but the runway is roughly eight weeks and shrinking.&lt;/p&gt;

</description>
      <category>shopify</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>ecommerce</category>
    </item>
  </channel>
</rss>
