It can be a daunting sentence for ecommerce merchants: “Buy one, get one free.”
Offering promotions to clients helps drive traffic and create loyalty among customers, but figuring out the most effective and efficient way to hand out these rewards, especially for large merchants, can be a challenge.
Your role as a web developer is to make these incredibly important promotions affordable, simple to use, and built in a way that doesn’t slow down a merchant’s online store.
Scripts are micro-customizations that let you write your own code to address the complex needs of enterprise-level merchants. Unlike apps, Scripts run custom code on Shopify’s servers – letting you focus on delighting your clients without having to worry about a lot of support or maintenance headaches.
Because Scripts run within Shopify’s environment, they also have the advantage of achieving tasks without resorting to workarounds that can lead to even larger headaches elsewhere.
The catch with using cutting edge tools is that resources can be scarce in the early days; that’s why today I’m going to walk you through how I helped Plus merchant Evy’s Tree tackle their promotion problems using Shopify Scripts.
You might also like: Creating Useful CSS Hooks in Liquid
Based in Santa Rosa, Evy’s Tree is a women’s apparel brand that makes high-end hoodies. While the company was working hard to build their reach, diversify their offers, and increase their yearly revenue year after year, something was holding them back.
Whenever they ran a ‘Buy One, Get One Free’ deal or offered tiered discounts to their customers, they ran into serious issues.
There are lots of applications that offer a variety of sales set-ups, but many were failing Evy’s Tree. Often, the apps were too clunky and difficult to configure for the sorts of promotions the merchant wanted to run – and sometimes the desired deals weren’t possible to run at all.
To make matters worse, many of the apps Evy’s Tree used worked by creating alternate products and variants with different prices. This not only clogged up the Admin, but also meant serious consistency issues were cropping up with inventory.
According to Evy’s Tree IT Lead Samantha Donohue, the way the company was doing promotions wasn’t flexible enough and was costing them money.
The problem is that most back-end systems make you choose between high availability and scalability, at the cost of flexibility. In this day and age, that just doesn’t work.
“At Evy’s Tree, our business is growing at an orders of magnitude rate year-over-year, and a big part of that is our strategy to utilize our data to respond to industry, customer, and seasonal trends in real time. The problem is that most back-end systems make you choose between high availability and scalability, at the cost of flexibility. In this day and age, that just doesn’t work.”
Find your new favorite Markdown editor in our roundup.
Can you say Scripts?
It was a no brainer that Shopify Scripts would offer the right solution to the problems Evy’s Tree was facing.
Scripts gives stores a way to write custom Ruby that is run on Shopify’s servers, and affects carts at a fundamental level, meaning things like line item level discounts can be applied, without the sort of hackery that existing applications need to perform.
Now that you’ve got an idea of what Scripts are at a high level, and why we thought it would be a good fit for this situation, I’m going to walk you through the steps we took at a technical level to implement them for Evy’s Tree.
Because we hadn’t dealt with scripts before, we first set out to learn as much as possible about them – how they were implemented, what limitations they had, and whether there were any best practices we should follow.
This meant reading through Shopify’s detailed scripts documentation, their open-source repository of sample scripts, and checking out the scripts demo store. The key things we learned at this stage were:
- Scripts are written in a stripped-down version of Ruby, and work by receiving an “input” cart, performing transformations on line items, and returning the resulting “output” cart.
- Scripts are standalone – you can’t make any external API or database calls to fetch additional information.
- The Shopify Scripts Team have developed a pattern for structuring Scripts code in a modular and reusable way, breaking the application of script logic down into Campaigns, Selectors, Partitioners, and Discounts. We’ll take a look at each of these in a second.
They say that the best way to learn is through doing, and I feel that that goes double for coding! Now that we had some idea on how scripts were put together, it was time to start playing around in a sandboxed environment and see what we could write ourselves.
We jumped into a test store, got the Script Editor app installed, and opened up the editor. (It’s worth noting that even though Scripts can only be used by Shopify Plus merchants in production, if you’re a Shopify Partner, you’re able to test them out on any development stores you create)
When you first open the app, it gives you the option of loading one of a range of template scripts into the editor. These give you a start on handling a bunch of common scenarios, such as offering a fixed dollar amount off certain products, or calculating bulk discounts.
As an example, here’s the demo “percentage off for a specific product” script:
Even if you aren’t super familiar with Ruby, reading through this should give you a good idea of how scripts operate. We loop through the line items of an
Input.cart object, check to see if the line item is for a specific product (and that it isn’t a gift card), and if the line item matches, we discount its price by 10 per cent. As a final step, we return the adjusted cart object by assigning it to
Using these simple template scripts as a base, we experimented with the parameters involved, and tested out what pieces of information were available from within the scripts. Doing this helped us acquire the following knowledge:
- You can only have a single script running on your store (“published”) at any one time.
- How to use the script editor’s “preview” functionality to test the scripts we were editing against simulated carts in real-time.
- It’s a good idea to be liberal with puts statements at each step of your code, to help debug what’s happening in your script in the Script Editor’s output console.
- You only have access to a limited amount of information about the cart, the products in the cart, and the current store and customer. For example, you can’t select products based on their membership in a particular collection, but you can select them based on tag, vendor, or product type.
3. Script Implementation
Now that we’d gotten our feet wet and had an idea of how a script was put together, it was time to attempt implementing a solution for the real-world promotion Evy’s Tree wanted to run: “Buy the set, get 10% off” a pair of newly released hoodies.
At this stage, we could have taken the approach of writing a one-off script for this promotion in the fashion of the template scripts – something pretty direct and simple that handled just this one scenario. However, there were a couple of things we knew:
- It would be useful for Evy’s Tree to be able to tweak the parameters of this script themselves, without having to constantly rely on us.
- It was likely that we’d want to run multiple promotions at the same time in the future.
- It was likely that we’d want to be able to expand the capabilities of the script down the line – for example, to easily swap out a percentage discount for a fixed dollar amount discount.
Given all of that, we decided to model our scripts on the patterns laid out by the Shopify Scripts team in their example scripts. This pattern uses an object-oriented, class-driven approach to break the different logical functions of scripts into isolated pieces. This makes it both easier to reason about what the different parts of your script are doing, and also easier to mix, match and share components between different scripts.
Here’s a quick overview of the different roles under this pattern:
- Selectors: objects that identify the line items and products that are of interest to your script. For example, a
PriceSelectorwould select line items based on their price, and a
TagSelectorwould select line items based on a product’s tags.
- Partitioners: objects that can “split up” line items based on your script’s needs. For example, if you were writing a “buy two, get one free” script, you could have an
InterleavedPartitioner, which splits a line item with a product quantity of three into two separate line items, with quantities of two and one. Your 100 per cent discount could then be applied to that second line item.
- Discounts: objects that take the line items identified by your selectors and partitioners and actually apply a discount. You could have a
PercentageDiscountapplying a percentage off a particular item, or a
MoneyDiscountto reduce the price by a fixed amount.
- Campaigns: a campaign object is the “container” for a specific promotion or discount you want to run. A campaign can be initialised with one or more selectors, partitioners, and discount objects that work together to implement the overall logic for the campaign. One Shopify Script could have multiple campaigns running at the same time.
Here’s an example of how all of these components can work together to create a series of campaigns, running at the same time in one script (for simplicity, the code that actually defines the selectors, partitioners, and discounts has been removed):
For us, once we’d decided to use this pattern to implement the “Buy the set, get 10% off” script, we actually found that it made designing and implementing the script much easier, as we could focus on each bit of logic individually. We started by setting out what we’d need from each component:
A selector capable of identifying the two hoodies that were part of the promotion. For simplicity, we decided to specify a list of relevant product IDs in the script, so we were able to use the
ProductsSelectorprovided by Shopify.
- A partitioner, which would check to see if all the products in the set were present in the cart. If so, it would return all line items to be discounted – if not, it would return no items. This would have to be custom written.
A discount, which could apply 10 per cent to each line item identified by the selector and partitioner. For this, we could make use of the
PercentageDiscountclass provided by Shopify.
- Finally, a campaign object to tie each of these components together.
As mentioned, we were able to reuse the selector and discount classes provided in the script example repository, meaning we only needed to write a
Finally, we added a couple of variables like
DISCOUNT_PERCENT to make things easy for Evy’s Tree staff to tweak and reuse this script, in case they wanted to run a similar promotion later on.
If you'd like, you can view the final script.
4. Theme implementation
We had the script up and running and had tested the logic, both within the Script Editor app, and when published on our development store. Now we needed to make sure that what customers saw in the theme and checkout matched what they were going to be charged.
Along with the Script Editor, Shopify introduced some new Liquid variables for exactly this purpose. The most important ones for the purposes of displaying discount information are
Using the first, we’re able to tell if a particular line item has been discounted by a script, and the second allows us to display the message generated by our script explaining why the discount was applied.
A final tweak we made for Evy’s Tree was on the checkout page. By default, Shopify Checkout displays line item discounts like this:
Since launching the promotion we’ve detailed above, we’ve come back to it a couple of times to extend the functionality or handle other edge cases.
For example, Evy’s Tree wanted to occasionally run promotions where a customer only needed to purchase a minimum quantity of items to be eligible for the discount, not one of each item in the set.
We could have implemented this as a separate script, but chose to leverage the code we already had and simply added a new
MinimumQuantityPartitioner that could slot into the existing campaign. Now, the store owners can determine the behaviour of the campaign by setting a simple variable at the top of the script, which affects which partitioner class is used.
We’ve also been able to tackle more complex scripts to handle new types of promotions – deals like “Buy X, Get Y for Z% off”, where the mix of eligible products and sale products can change or have complicated rules around them.We had to resurface of our long-forgotten algebra skills for some of the more complex promotion logic.
As we develop more and more experience with scripts, we’ll be able to handle more complex scenarios. This will be helped by the library of reusable classes and components we’re putting together (and contributing back to the public scripts repository), and also by the module pattern advocated by the Shopify scripts team.
A scalable solution for your Plus clients
As you’ve hopefully gleaned from this post, Scripts offers your most successful clients a way to get advanced custom functionality, without compromising flexibility or maintainability. It’s a win for you as a developer, too – you don’t have to worry about maintaining applications or keeping servers running. And it’s improving all the time – the roadmap for Scripts has some exciting developments on the way, far beyond the discounting examples covered here.
Whether you’re interested in using Scripts on your next client project, or are just curious about Shopify’s newest tool, I hope this case study has been helpful.
Have a question for me? Reach out in the comments section below.
You might also like: Using Metafields in Your Shopify Theme