Shopify App Store · Public app

Swift AI Collections

A published Shopify app that keeps every collection in the order that makes the store the most money: ranked on revenue, conversion, margin, stock health, and click-through, re-sorted hourly, and safe to run against a catalogue of tens of thousands of products. Built solo, from the ranking engine to the App Store listing.

View on the Shopify App Store

The problem

Shopify sorts a collection by one thing at a time. Merchandising is never one thing.

Best selling, price, newest: pick one. A real merchandiser weighs what is selling now, what earns margin, what is in stock in every size, and what shoppers click. Then they do it again tomorrow, for every collection. The apps that automate this cap how many collections you can manage and re-sort once or twice a day, because pushing a new order to Shopify is slow. This app is built around making that push cheap.

Swift AI Collections preview of a fragrance collection re-ranked by recent sales: 40 moves applied in one API batch across 2,853 products, with products rising and falling
Timeline2026 · live since August
RoleProduct, engineering, infrastructure, App Store submission
StackTypeScript • React Router • PostgreSQL • Shopify
57 / 57real production sort runs the planner reproduced exactly
3.8×fewer moves sent to Shopify on identical inputs
105 msto plan a re-sort of a 50,000-product collection
2,300+collections kept in order on a 47,000-product store

The fastest API call is the one you do not make.

Shopify reorders a collection 250 moves at a time, and each batch returns a job that has to finish before the next can start. Push the full order of a 5,000-product collection and that is twenty sequential round-trips. That cost is why merchandising apps re-sort once a day and cap the collections you can manage.

But a daily re-sort rarely changes much. The products already in the right relative order can stay put, and the minimum number of moves is the collection size minus its longest increasing subsequence. The planner computes exactly that. On 57 real sort runs from a live 47,000-product store it cut 84,916 moves to 22,299 and 382 API batches to 137, and 4.7 to 7.5 times fewer moves on the collections of 3,800 to 13,200 products where it matters most.

Cheap re-sorts are what the pricing rests on: 100 managed collections re-sorted hourly on the $19 plan, where the category leader’s entry plan manages five, once a day.

250moves Shopify accepts per reorder call, each an async job to poll
1batch, instead of 20, for a typical day’s change on 5,000 products
18.5s → 105msplanning time at 50,000 products after replacing an O(n·m) inner loop
Preview first

Nothing touches the store until the merchant has seen the exact moves.

Preview computes the full proposed order and the minimum set of moves to get there, without writing anything. Every product shows its score and the signals behind it, so a merchant can see why the new bestseller jumped and why last season’s hero slid. Applying sends only the moves that preview listed. If the result is wrong, any run rolls back to the snapshot taken before it.

Swift AI Collections collection screen showing the chosen strategy and schedule, a sort preview of 85 moves over 113 products in one API batch, and the proposed order with the signals behind each score
Position-adjusted click-through

A product in slot one gets clicked because it is in slot one.

Shopify reports no per-position engagement for a collection, so the app ships its own web pixel to record impressions, click position, add-to-cart, and purchase. The position chart shows why that matters: attention falls off a cliff after the first few rows. Click-through is divided by what its position would have earned anyway before it feeds back into ranking. Without that correction, whatever is on top stays on top forever.

Swift AI Collections analytics with impressions, click-through, revenue per impression, data freshness per source, and a click-through-by-position chart
Where money leaks

Stock that cannot be bought, stock nobody buys, stock nobody can find.

The products view leads with the three states that quietly cost a merchandiser money. Sold-out products still sitting in prime slots, products with no sales in thirty days, and products no sort has placed yet each get a count and a one-click filter. They are the first things a good merchandiser would check by hand, and on a catalogue of tens of thousands of products nobody can check by hand.

Swift AI Collections products screen flagging sold-out products still taking up slots, products with no sales in 30 days, and products no sort has placed yet

How the ranking avoids fooling itself

  • A product with one view and one order shows 100% conversion. Sparse products are shrunk toward the collection average, weighted by how much evidence they have.
  • Click-through is divided by what its position would have earned anyway, before it counts.
  • Strategies are weighted blends of revenue, momentum, conversion, margin, and stock health, and the merchant can see the weights.
  • Rules sit on top: pin, hide, boost, bury, and cap how many products per vendor appear in a row.

A/B tests that tell you when there is not enough data yet.

Two strategies can run against each other. With the theme extension installed, visitors are bucketed on the storefront and both orders are served at once, a real randomised experiment. Without it, the app falls back to alternating time slices blocked by day of week, because a raw Monday-versus-Tuesday comparison measures the calendar.

The analysis is Bayesian: the probability that B beats A, a credible interval on the lift, and the expected loss of shipping the wrong one. Until the data can support an answer, the dashboard says so, rather than printing a p-value that invites peeking.

Guardrails

It rewrites a live storefront every hour, so it has to be boring to trust.

Rollback before every apply.

The current order is snapshotted before a run writes anything, so any sort can be undone, including one that looked right in preview and turned out wrong on the storefront.

A guardrail calibrated on real runs.

A run that would reorder more than 60% of a collection aborts instead. The median real run reorders 5.9% and the 90th percentile 22.5%, so none of the 57 production runs would have tripped it. A half-synced signal table rewriting a collection wholesale would.

One writer per collection.

Shopify corrupts a collection’s order if two reorder batches overlap. A per-collection advisory lock means a scheduled run and a manual one can never interleave.

Aggregation stays in the database.

Pixel events on a big store run to millions of rows. Folding them in JavaScript would exhaust a 768 MB heap on a server shared with five production sites, so every rollup is one set-based SQL statement.

Bulk Operations for ingestion.

Catalogue and order syncs run as Shopify Bulk Operations, outside the API rate limit that paginated queries drain on any store beyond a few thousand products.

Freshness is shown, not assumed.

Catalogue refreshes every six hours, orders every three, traffic daily. When a shop falls behind its promised cadence, the overview page says so with real numbers instead of letting "hourly" quietly mean "every four hours".

Loyalty add-on · rolling out

A loyalty programme that lives in the store, not next to it.

Most loyalty apps are a side channel: earn points, open a widget, copy a code, paste it at checkout. The loyalty add-on changes the prices a member actually sees. A Gold member browsing the store sees Gold prices on product pages, collection grids, and the cart, in the first paint, and pays them at checkout without entering anything.

That works because the tier is written to customer metafields that the theme, the storefront script, and a Shopify Function discount all read. Three separate pieces of code compute a member price, and a test holds them to the same cent across 180,000 price and percentage pairs. The Function compiles to 4.7 KB of WebAssembly.

What the add-on does differently

  • Member prices shown while browsing, and applied at checkout by a Shopify Function, with no code.
  • Spend any amount of points at the cart on every Shopify plan, not only Plus.
  • A cap on how much of an order points can pay for, so a big balance never zeroes an order the merchant still has to ship.
  • Member-versus-guest revenue reporting that says plainly it is observational, and refuses to print a lift under ten orders a side.
Under the hood

The stack, named honestly.

  • React Router 7
  • TypeScript
  • Prisma
  • PostgreSQL
  • Polaris web components
  • Shopify App Bridge
  • Bulk Operations API
  • Web Pixels API
  • Theme app extension
  • Shopify Functions (Wasm)
  • Shopify Billing API
  • nginx + systemd

What this case study should prove.

I can take a merchandising problem from the storefront all the way down to the API limits underneath it, find the constraint everyone else designed around, and remove it. Then I can check the result against a live store’s own history before claiming a number, and cut the number when reality comes in lower. An earlier estimate of 20× fewer moves became 3.8× once it met production data.

It is a public Shopify app with billing, compliance webhooks, protected-data review, and a live merchant sorting thousands of collections through it every day.

View on the Shopify App Store

If you remember one thing

  • Collections ranked on revenue, conversion, margin, stock, and position-adjusted click-through.
  • A minimum-move planner that re-sorts hourly where others manage daily.
  • Preview, rollback, and guardrails on every run against a live store.
  • Bayesian A/B testing, and a loyalty add-on that prices members in the first paint.