Expiry date tracking: FEFO rotation and the waste math
Expiry date tracking for small catalogs: FEFO vs FIFO rotation, a worked waste calculation, markdown-before-expiry triggers, and a spreadsheet method.
InventoryX is in development and not available today. ClearStock is the live Shopify app.
Expiry date tracking answers a question normal inventory counts can't: not "how many units do I have?" but "how many units do I still have time to sell?" For anything dated — food, supplements, cosmetics — the units and the days are both stock, and managing only the units is how stores end up holding perfectly counted inventory that has to be thrown away. The core disciplines are rotation (FEFO) and dated buying (order against remaining shelf life, not just remaining units).
FEFO vs FIFO: the difference that matters
FIFO — first in, first out — ships the oldest received stock first. It's the default in every fulfillment process, and it's correct whenever arrival order predicts age.
FEFO — first expired, first out — ships the stock with the earliest expiry date first. The two disagree whenever a later delivery carries shorter-dated stock, which happens constantly with resold goods: a supplier clearing end-of-life production slots will happily ship you batch B with 4 months of life while batch A (bought earlier, longer-dated) sits behind it. Picking FIFO ships the wrong units.
The practical rule for a small operation: rotate FEFO where it's cheap to do — physical placement and a dated receiving log — and let your pick process stay FIFO-shaped. If the earliest-expiring units are always the ones at the front of the shelf (or flagged in the pick list), FIFO picking is FEFO. The failure mode isn't the software; it's a restock where fresh units got stacked in front of short-dated ones.
The waste math of ignoring expiry dates
Run the arithmetic on one SKU, because the loss pattern is invisible unit-by-unit. Suppose you sell a chipotle hot sauce, SKU SAUCE-HOT-CHIP-5OZ, at 2.5 units/day, and you buy in 6-unit cases with a 12-month shelf life from production. A supplier lot arriving with 4 months of remaining life carries 120 units:
- Days of cover you can actually sell: you need remaining life ≥ your normal stock turn. At 2.5/day, 120 units takes 48 days to sell — inside the ~120 days of life, but only if rotation is perfect and velocity holds.
- Now slow the velocity to 1.6/day (end of summer): 120 units takes 75 days. Still fine — barely.
- Now suppose those 120 units arrived with only 60 days of life: you'd sell ~96 and lose 24 units to expiry. At a $6.20 unit cost, that's $148.80 of cost written off on a single receipt — plus the disposal time, and the count corrections when expired units are pulled.
Two conclusions fall straight out of the arithmetic. First, shelf life is a buying constraint: the usable depth of any order is min(days of cover you want, days of life remaining). Second, the waste happens at receiving, not at expiry — the decision that doomed those 24 units was accepting a 60-day lot without checking it against your velocity. A receiving discipline of "record the expiry date per lot, compare against days-of-cover" costs one column in a spreadsheet and catches it. If you already keep an inventory spreadsheet, expiry becomes one more column, and your standard replenishment reports stay unit-based while the dated risk lives in the log.
Markdown before expiry: the trigger schedule
Expired stock isn't a markdown problem — it's a timing problem, and the trigger dates are computable. A workable schedule for dated goods:
| Remaining shelf life | Action |
|---|---|
| Comfortably above your sell-through window | Full price; no flags |
| ~2× your days of cover | Watch: confirm velocity; stop reordering this lot |
| ~1–1.5× days of cover | Markdown or bundle decision — price to accelerate, not to protect margin |
| Below your days of cover | Deep markdown, donation, or write-off — recovery mode |
The percentages depend on your margin structure; the principle doesn't. A jar of sauce at $14 retail with 3 weeks of life and 5 weeks of stock is worth 40% off today, because full-margin zero next month loses to partial-margin something today. Units that miss the trigger window don't become slow sellers — they become the dated subset of dead stock, except with a zero-resale floor: expired goods can't even be liquidated through the usual channels, so the action ladder has a hard bottom.
One caution on the opposite error: don't mark down long-dated stock out of anxiety. An unopened skincare item with 18 months of life is fine at full price. The trigger is the ratio of life to your velocity, not the calendar alone.
What to record, and where
The minimum expiry-tracking record, per lot received:
| Field | Example | Why |
|---|---|---|
| SKU | SAUCE-HOT-CHIP-5OZ | Links to your catalog |
| Lot code | B-240317 | Ties the date to a physical batch (the producer prints it) |
| Expiry / best-before | 2026-11-30 | The rotation key |
| Units | 120 | Your count |
| Location | Bin A-02-3 | So the earliest-dated units are findable and front-facing |
With that log, a weekly five-minute review sorts by expiry, computes expiry − today against each SKU's days of cover, and flags anything inside the markdown trigger band. Products carrying a period-after-opening date instead of a hard expiry (common in cosmetics) work the same way with one twist — the clock starts at first sale, not at receiving, so the log needs an "opened" note per unit, which is only practical for higher-priced items.
GS1 standards encode batch/lot numbers and dates as barcode attributes alongside the product identifier (GS1 Barcodes, gs1.org/standards/barcodes — accessed October 2026), which is why supplier case labels carry both codes. Reading the date code at receiving and logging it is where shelf life management starts; apps and ERPs that automate expiry tracking exist for larger operations, but at small scale the log-and-review routine does the same job.
The tooling honesty note
InventoryX doesn't track expiry dates in V1 — dated-lot fields are ERP territory and outside its scope, which covers the replenishment edge: out-of-stock days per SKU, estimated lost sales, and a weekly buying list. For dated goods that combination still matters (a SKU that repeatedly goes out of stock pushes you into accepting short-dated lots you shouldn't), but the expiry layer itself stays in the receiving log for now — the same position as the lot log it usually shares a tab with.
Common questions
What is FEFO in inventory management? FEFO — first expired, first out — rotates stock by expiry date, shipping the earliest-dated units before later ones regardless of when they were received. It differs from FIFO (first in, first out) exactly when a newer delivery carries shorter-dated stock, which is common with resold goods. For anything perishable or dated, FEFO is the safer rule.
How do I track expiry dates without expensive software? Keep a receiving log with one row per lot: SKU, lot code from the packaging, expiry date, units, and shelf location. Review it weekly, sorting by expiry, and compare each SKU's remaining life against its days of cover to trigger markdowns in time. Physical rotation — earliest-dated units at the front — makes the pick process follow the log automatically.
When should I mark down stock before it expires? Use remaining life as a multiple of your days of cover. Around 1–1.5× cover, make the markdown or bundling decision; below 1×, go to deep discount, donation, or write-off. A markdown that moves units weeks before expiry recovers more than full-price confidence followed by a zero-recovery disposal.
Does Shopify track expiry dates? Not natively as of October 2026 — Shopify counts units per SKU and location, without a dated-lot layer (Shopify Help Center, "Inventory and locations," accessed October 2026). Small sellers typically track expiry in a receiving-log spreadsheet keyed by lot code, while apps and ERP systems add automated expiry fields for larger or highly regulated operations.
What's the difference between expiry date and period after opening? An expiry date applies from production and is printed on the packaging; a period-after-opening (PAO) clock starts when the customer first opens the product, so it mostly affects what you promise on the product page rather than your warehouse rotation. For stock management, the printed expiry or best-before date is the one that drives FEFO rotation and markdown timing.
Next step
Take your three fastest-selling dated SKUs and check one thing in your storeroom: is the currently-front-facing stock also the earliest-dated stock? If not, fix the rotation this week, then start the five-column log with whatever's open in front of you. The velocity side of the trigger math — how fast you can realistically sell through a lot — is the same days-of-cover reasoning that drives your reorder cadence decisions generally.