The merchandising lead opens two cards on the same September board. Card one is a 20% sitewide automatic discount marketing already scheduled in the Shopify admin. Card two is the founder brief from Monday: buy two, take 40% off the third, free shipping over £75, and a gift pot once the cart hits £120.
Those are two different discount engines. Shopify automatic discounts and discount codes live in the admin Discounts page and cover percentage, fixed amount, Buy X Get Y, and free shipping. Discount Functions are app-defined types that run as Shopify Functions when the cart changes, so one rule can hit lines, the order, and shipping together, or apply tier and tag logic the native form cannot encode.
Choose the engine per promo before the freeze. Native automatic or code first when the rule fits. A public Function app or a Plus custom Discount Function only when the rule is genuinely custom, and only if you can test it on peak-sized carts while you still have time to roll back.
Introduction
This article teaches you to choose native Shopify automatic discounts or Discount Functions for each Black Friday promo by mapping stacking, BOGO, tiers, gift-with-purchase, Plus access, and peak performance before the freeze. Pairing a checkout banner with backend checkout logic is a different job, and it belongs in When to Use Checkout UI Extensions vs Shopify Functions.
The dated walk of checkout, inventory, fulfilment, and support gates sits in A Shopify Black Friday Readiness Checklist for Brands That Already Scale. This post stays on the discount method for the offers you already decided to run.
Merchant and developer threads in autumn keep returning to the same gap: a sitewide automatic that stacks with a code nobody re-checked, a gift that never appears as a line, or a Function that cannot see a discount code and a shipping country in the same run. The community signal is decision friction, not a missing campaign idea.
The decision merchants face
Shopify gives you three ways to reduce price, and they are easy to brief as one “promo”:
- Automatic discounts apply when the cart meets the conditions. The buyer never types a code.
- Discount codes apply when the buyer, an email, or an abandoned-checkout recovery message supplies the code.
- App discounts, including Discount Functions, appear as extra types on the Discounts page after you install an app. Shopify then executes the Function to calculate the reduction.
Native automatic and code types are the same four shapes: percentage, fixed amount, Buy X Get Y (or Spend X Get Y), and free shipping. Combination settings decide whether a product discount, an order discount, and a shipping discount can sit on the same order.
A Discount Function is a new type. Shopify’s Discount API can return product and order operations from cart.lines.discounts.generate.run, and shipping operations from cart.delivery-options.discounts.generate.run. One Function can therefore do work that would otherwise be three native discounts with three combination matrices.
That power is why teams brief Functions too early. A 20% collection automatic with combination boxes written down is faster to test, cheaper to staff, and easier to turn off on 2 December than a WASM module you still have in review.
Black Friday discount engine
Criteria that actually matter
Write one row per Black Friday offer. The columns below are the job. If a cell is blank two weeks before the freeze, the offer is not ready.
Stacking and combination classes
Shopify applies discounts to the subtotal before tax. Combination is class-based: product, order, and shipping. Two product discounts do not stack the way two different classes can.
Community prep threads keep landing on the same September homework: list every code, automatic, and free-shipping rule that will be live, then inspect each pair. A 20% automatic on the collection plus a 25% welcome code can become a 40% effective cut when both combination boxes stay ticked. That is a written policy, or it is an accident.
Discount Functions still participate in that model. The Function declares discount classes. Those classes decide which targets Shopify runs and how the Function sits next to native automatics and codes. A custom type that claims product, order, and shipping in one owner can hide the pair you thought you had isolated.
Replay last year’s peak SKUs against this year’s planned stack on a spreadsheet before you trust the admin preview. Cost per item has to be on the variant, or no report will tell you whether the pair still leaves contribution.
BOGO, tiers, and gift-with-purchase
Native Buy X Get Y covers “buy two, get the third at a set percentage or amount” when the rewarded item is already a catalogue line the buyer can add. Spend X Get Y covers a threshold on the same shapes.
The brief leaves native once the third unit needs a different rate from the sixth, once customer tags change the band, or once the gift is a SKU that should appear only after the threshold. Volume apps that only expose static percentage steps hit the same wall; a Discount Function can encode a formula or a tag table the admin form cannot.
Gift-with-purchase is the usual trap. Zeroing a qualifying item the buyer already added is a discount job. Inserting a new gift line with its own title and image is a cart-shape job. Cart Transform can expand, merge, and update lines. Adding a brand-new line is still a requested gap, which is why gift apps still mix Functions with storefront cart calls. If the founder wants a pot that “just appears”, name the add path on the row before ads mention the threshold.
Codes versus automatic
Automatic discounts are the right default for a sitewide or collection sale the whole weekend. Codes are the right default when the offer is for a list, an affiliate, or an abandoned checkout, because you can expire the code without touching the automatic that still sits on the storefront.
Mixing both is where BFCM goes quiet-then-loud. Recovery emails can attach a code to a cart that already received the automatic. Combination settings decide whether that is a second reduction or a rejected code. Test guest checkout, logged-in checkout, and the recovery path on the same SKU.
A Function that must reject a code for some shipping countries has a second constraint. The cart-lines discount target can see entered codes and often cannot see delivery address. The delivery-options target can see country and may not run once a code is present. If the Black Friday rule needs both inputs in one decision, treat that as a Function-design risk and settle it in October, or drop the geo rule from the promo.
Plus versus a public Function app
Stores on any plan can install a public App Store app that contains Functions. Only Shopify Plus can run a custom app that contains Shopify Function APIs. Shopify Help states the same split for building your own custom discounts app.
Checkout UI extensions, payment customisation, and delivery customisation remain Plus checkout work. They do not unlock a better percentage automatic. If the plan question is still open, use Choosing the Right Shopify Plan before you treat an upgrade as a November discount patch.
A public Function app is still a production dependency at peak. You inherit the app’s WASM budget, its input queries, and its support hours. Prefer an app you already run in ordinary weeks over a seasonal discount install you will forget in January.
Performance at peak
Shopify executes Discount Functions inside checkout. Delays and failed runs affect whether the buyer can purchase. Shopify recommends Rust for large carts because JavaScript Functions are more likely to hit instruction or memory limits when the cart is full.
Function order is fixed: cart lines, Cart Transform, line discounts, then later delivery discounts and validation. A slow Discount Function sits on the purchase path, in front of fulfilment and payment customisation. Peak weekend is a poor first load test.
If you ship a custom Function, replay carts with last year’s line counts, plus a worst-case gift or bundle expansion, and read the Function run logs before the freeze. If you cannot name the last successful run against a large cart, the offer still belongs on a native automatic.
Framework
Use this matrix on the promo sheet. One row per offer. The recommendation is the first column that the rule actually needs.
| Criterion | Native automatic or code | Public Discount Function app | Custom Discount Function (Plus) |
|---|---|---|---|
| Rule shape | Percentage, fixed amount, Buy X Get Y, free shipping | Tiers, tags, bundles, or mixed product and shipping in one type the app already ships | Brand-specific formula, membership table, or a class mix no public app can configure |
| Stacking | Write product, order, and shipping combination pairs; test each pair on a live cart | Read the app’s discount classes and how they sit next to native automatics | Declare classes on purpose; one Function can replace three native discounts |
| BOGO and tiers | One rewarded rate, rewarded item already in catalogue | Stepped volume or category thresholds the app exposes | Formula or tag-driven bands the admin cannot store |
| Gift-with-purchase | Discount or zero a line the buyer already added | App that owns the gift add path and inventory | Function plus Cart Transform or cart API, with the add path named |
| Who can build it | Any plan, Discounts page | Any plan, App Store install | Plus custom app only |
| Peak risk | Combination mishap, recovery code stacking | App WASM limits, leftover seasonal install | Untested large-cart runs, input-query gaps, deploy during freeze |
| Choose this when | The brief fits a native type and you can write the pairs | The app already matches the brief and has run on this store | The brief is unique and the Function is in production before the freeze |
Recommendation by scenario
Sitewide or collection percentage
Use a native automatic. Put the collection or product list on the discount, write the combination boxes, and keep marketing codes on a separate class or with combinations off. This is the card you can schedule, screenshot, and turn off on 2 December without a deploy.
Simple BOGO on the same SKU
Use native Buy X Get Y. “Buy two, get the third at 40%” is an admin rule when the third unit is the same product. Save Functions for a different rate on unit six, or for a gift that is a different SKU.
Tiered volume or member pricing
Start with a public Discount Function app you already trust if the tiers match its configuration. Commission a custom Discount Function on Plus only when the table is brand-specific and the app would need a rewrite. Either path needs a large-cart Function log before paid traffic.
Gift-with-purchase plus free shipping
Split the brief. Free shipping over £75 is a native shipping automatic or code. The gift pot is a separate row: native only if the pot is a catalogue item the buyer adds, otherwise an app or Function whose add path you have already tested with inventory. Do not hide both rules inside one untested Function the week ads start.
Code for a list, automatic for the weekend
Keep the weekend sale automatic. Give the list a code on a different class, or with combinations disabled against the automatic. Test abandoned-checkout recovery on a cart that already received the automatic, because that is the path that silently stacks.
If checkout itself is dropping purchases, fix that path first with How to Fix Shopify Checkout Abandonment When Your Funnel Looks Healthy. A Function that calculates a perfect discount still fails if Shop Pay or the payment step cannot complete.
The brief arrived in November
Simplify. Peak week is for combination audits and live test orders, not a new discount type. Park the gift pot or the tag table until January. The freeze in the readiness checklist exists so you stop inventing engines while the warehouse is already loading.
What to lock before the freeze
Put these dates on the same sheet as the offers. They sit beside the inventory work in How to Stop Shopify Oversells During Black Friday Flash Sales, because a gift SKU with no available-to-sell number is an oversell you scheduled as a promo.
- Black Friday minus 21 days: every offer has an engine (native automatic, code, public Function app, or Plus custom Function) and a named owner.
- Black Friday minus 14 days: combination pairs tested on guest, Shop Pay, and recovery; Function run logs exist for a large cart; gift add path proven with inventory.
- Black Friday minus 10 days: live test orders show the expected discount lines and shipping, with no staff workaround.
- Freeze: no new discount apps, no Function deploys, no combination edits except a documented hotfix with rollback.
Seasonal badge, timer, and gift apps are easy to install in October and easy to forget in January. If the discount engine is an app you only need for three weeks, write the uninstall date on the same row as the start date.
This reflects patterns we see regularly across the Shopify merchant and developer community.
Conclusion
You can now choose native Shopify automatic discounts or Discount Functions for each Black Friday promo. Map stacking classes, BOGO and tier shape, gift-with-purchase add path, code versus automatic, Plus versus a public Function app, and peak Function performance, then freeze whatever you did not finish.
Start with the admin Discounts page when the rule fits. Use a Function, or a Function app, when one type must do work the native form cannot, and only when the run has already survived a large cart. The founder brief can wait until January if the engine is still a spec.
If the promo sheet needs a Discount Function or a checkout-adjacent build scoped and shipped before the freeze, start with Shopify UI Extensions and Functions or book a discovery call and we will map each offer to an engine the store can actually run on the Friday.



