Run a store long enough and you end up with two kinds of discounts running at once. There is the sale: you mark down a collection, set the compare-at price so the strikethrough shows, and watch the traffic come in. And there is the code: the 10 percent welcome offer from your email popup, the influencer code, the loyalty voucher, the sorry-we-were-late code your support team hands out. Each is fine on its own. The trouble starts the moment one shopper holds both, because Shopify will happily land the code on top of the markdown, and your 40 percent sale quietly becomes a 46 percent sale you never approved.
The community threads on this are among the most repeated in Shopify's forums, and they all ask the same thing in slightly different words. "How do I exclude sale items from discount codes?" "Stop customers using welcome codes on sale products." "Discount code applying on top of already reduced prices." The standard answers are a shrug in three parts: there is no native toggle, you can rebuild your discounts around a special collection, or you can install an app. Merchants are not asking for anything clever. They want the oldest rule in retail, codes do not apply to sale items, and Shopify has no switch for it.
What Shopify actually does, and where it stops
Give the platform its due first. Shopify's discount engine handles the individual pieces well. Codes are easy to create and can be gated by collection, minimum spend, or customer segment. Automatic discounts apply cleanly with nobody typing anything. And since discount combinations arrived, you can even decide which classes of discount are allowed to meet: product discounts, order discounts, and shipping discounts each carry a combines-with setting, so two offers you built as discounts can be told to stack or to keep apart.
The wall is that a sale made by editing prices is not a discount at all as far as that engine is concerned. When you run a promotion the way most stores do, by lowering the price and keeping the old price as compare-at so the strikethrough shows, the discount engine never hears about it. The combination settings govern how discounts combine with other discounts. They say nothing about marked-down prices, because to Shopify those are just prices. So a sitewide 10 percent code applies to your 40 percent markdowns exactly as it applies to full-price items, and there is no checkbox anywhere that says exclude sale items. The one screen that looks like it should hold that rule does not.
The workarounds all route through eligibility. You can build an automated collection keyed off the compare-at price, so items on sale gather in one place, then point your codes at the inverse: a full-price collection the code is allowed to touch. It works the way a rope bridge works. The collection is only as accurate as your compare-at discipline, a code cannot always be re-scoped after it has been issued, and every code your apps generate on your behalf, the welcome popup, the loyalty program, the review-for-discount flow, is born sitewide unless each app happens to offer its own exclusion setting. Meanwhile checkout will now accept more than one code on an order when the combination settings allow it, which makes the stack deeper, not shallower.
A markdown and a code are both discounts to your margin. Only one of them is a discount to Shopify.
Why doing it by hand never keeps up
The by-hand version starts with good intentions and decays fast. You build the full-price collection, re-point your main codes at it, and for a week the rule holds. Then the sale rotates: twenty items come off markdown, thirty go on, and the collection is only right if every compare-at price was set and cleared correctly, which is the same discipline problem as the price change you make one product at a time. Then marketing launches a code for a creator and forgets the eligibility step, because scoping a code to the right collection is a manual habit, not a rule. Then the popup app issues a fresh batch of welcome codes, which never knew about your collection in the first place. Each miss is invisible at the moment it happens. You find out at the end of the month, in the margin report, or on a busy weekend when your best sellers go out the door at 46 percent off.
And underneath the mechanics sits judgement no toggle could hold anyway. Some codes should ignore the rule: a service-recovery code your support team sends after a late order probably should work on anything, sale items included, because the goodwill matters more than the margin. A creator code during a launch might be allowed to touch the sale collection deliberately. A loyalty voucher might be allowed to stack up to a floor and no further. The real policy is never simply codes off sale items, full stop. It is codes on sale items on these terms, and the terms differ by code, by campaign, and by how much margin the markdown already gave away.
What the automation actually has to do
The real job is not one more discount setting. It is a policy that runs everywhere discounts are born and everywhere they land: read the rule in plain language, keep the sale and full-price collections true automatically, scope every new code at the moment it is created, including the ones your apps mint, and guard checkout so that when a code meets a markdown the shopper gets the best discount you actually meant, not the deepest stack that slipped through. As a Dugong playbook, in plain prose, it reads like this:
# trigger
On any new discount code, any product moving on or
off sale, and any checkout where a code meets a
marked-down item
# steps
1. Read the policy: codes do not apply to sale items
unless the code itself says otherwise
2. Keep the on-sale and full-price collections true on
every price and compare-at change
3. Scope each new code on creation, including codes
minted by popup, loyalty, and review apps
4. Guard the checkout: when a code lands on a markdown,
apply the single best discount and drop the rest
5. Tell the shopper plainly which discount they got,
and why the code did not stack
6. Hold for review the exceptions: a support code on a
sale item, a stack that slipped through, a floor breach
Six lines. The compiler fills in the rest: watching price and compare-at changes so the collections never drift, catching each code at birth rather than repairing it after it leaks, resolving the best single discount instead of letting checkout arithmetic decide, keeping every combined discount above the floor you set, and writing the shopper a plain sentence at checkout so the declined stack reads as policy rather than a bug. The merchant never re-points a collection or audits a popup app again. They wrote the rule once and let the compiler enforce it on every code and every cart.
Why this is a compiler problem, not an app problem
There are apps that patch pieces of this, and a store bleeding margin every promotion should look at them. But the thing that makes the rule safe is not a per-app exclusion checkbox. It is judgement about a specific code meeting a specific cart: is this the support code that is allowed through, does this creator code plus this markdown cross the floor on this product, should the shopper get the code or the sale price when the code is actually the deeper of the two, and did the eligibility collection keep up with last night's reprice. Those answers change per code, per product, and per campaign, which is exactly what a fixed checkbox handles badly and a person cannot track across three apps and a promotions calendar.
A natural-language compiler fits because the rule was never a single setting. It is a watcher on your catalog, a gate on code creation, and a resolver at checkout, all reading one policy you wrote in a paragraph. It sits in the same family as the buy-more-save-more discount Shopify won't tier, where several discounts have to resolve to the one correct break instead of competing, and the sale you start by hand, which decides when items go on markdown in the first place and therefore when the exclusion has to start guarding them.
Picture an apparel store heading into a clearance weekend. Forty percent off the outgoing season, the welcome popup still live, a creator code running for the new season's launch, and a handful of service-recovery codes in the wild. On the native setup that is a leaking full-price collection, three apps with three different exclusion settings, and a support inbox asking whether to honor the order that stacked to 52 percent off. With the automation it is one paragraph of policy: the popup and creator codes stay off the clearance rack, the service codes go anywhere, every combined discount stays above the floor, and the one genuinely ambiguous order lands in front of a person before it ships instead of after it is refunded. Same promotions, none of the leakage.
The workflow worth building this week
If you run sales and codes at the same time, which is every store by its second summer, this is the automation to set up now, because the cost is already in your numbers and labeled as something else: a conversion win here, a margin miss there. It pairs naturally with the sale you start by hand, which controls when markdowns begin and end, and the free gift that never lands in the cart, where the cart also has to carry a rule the platform leaves to you. It feeds the same flywheel as the Shopify automations no one builds, where the compounding work is the guarding nobody gets around to.
Describe it the way you would brief a careful store manager: sale items are anything with a live markdown, codes do not touch them unless I name a code that can, never let two discounts push an item below this floor, give the shopper the best single discount and tell them why, and bring me anything that does not fit. That is the whole brief. The compiler does the watching, the scoping, the resolving, and the explaining. Your sale stays the discount you planned, your codes keep converting at full price, and the two never quietly multiply again.
If you are a Shopify merchant who has stopped the double-dip for good, or you
have a story about the stacked weekend that cleared your margin, the inbox is
open: field-notes@dugong.live. We are collecting case studies for
the next issue.