

6 yrs live
20+ API migrations
5,000 webhooks a day
0 downtime from a version change
built for shopify
AI Product Bundle Builder carries Shopify's Built for Shopify designation.
Shopify awards it to apps meeting its highest standards for performance, design and integration. It is audited against measured performance on real merchant storefronts, and it can be taken away. That is not a badge we gave ourselves.
4.7
rating, 21 reviews
650+
merchants on this app
0-4 yrs
0-4 yrs

20%
20%
5
5
100+
100+
why we published this
Our sister brand Essenify publishes six apps in the Shopify App Store. They are Ruby on Rails applications serving close to nine hundred independent merchants, each with their own catalogue, their own currency, their own theme and their own idea of what a bundle should do. The oldest has been live since May 2020, and you can install any of them in ninety seconds.
We are not writing this to sell you Shopify work. We are writing it because these apps are the clearest evidence we have of how we build and how we maintain, and unlike a client project, we can show you every part of it.
the constraint
A client project has a client. Scope gets agreed. Deadlines move when they have to. If something breaks at 2am on a Sunday, there is usually a conversation about it on Monday. An app store has none of that.

The platform sets the deadline
Shopify ships a new API version every quarter and supports each stable version for a minimum of twelve months. Miss the window and requests stop working. Nobody raises a ticket. The date arrives, and either you migrated or your merchants wake up to a broken store. Since May 2020 that has happened more than twenty times to our bundle app alone.

The quality bar is external
Built for Shopify is awarded by Shopify, audited against measured performance on real merchant storefronts, and it can be taken away. We did not set the standard and we cannot grade ourselves against it.

Every merchant has production data
There is no staging environment where a mistake is cheap. A bad migration touches hundreds of live stores taking real orders, in real currencies, on a real Tuesday.
the engineering, honestly
Multi-tenancy under load, quarterly platform migrations, and webhook volume that arrives out of order and twice.

01
Multi-tenancy under real load
Six applications, close to nine hundred stores. Each merchant's data is isolated, each has their own configuration, their own installed theme, and their own set of active bundles. A single deploy has to be correct for all of them at once.
Every tenant-owned table is scoped by shop_id. It is the single partitioning key across the schema, covering products, bundles, configuration and inventory sync records. That scoping is also what handles merchants with unusually large catalogues: their queries are narrowed to their own rows from the start, so catalogue size does not create contention with anyone else's data.
On the processing side, each shop runs its own queue, so jobs are worked through with per-shop priority instead of competing in one shared queue. A large merchant's bulk sync or bundle recalculation does not delay processing for a smaller shop sitting on the same deploy.
The bundle app alone supports fixed bundles, mix and match, build your box, volume tiers, Buy X Get Y, and frequently bought together, with inventory syncing per SKU so a bundle cannot oversell a component that has run out. That inventory sync is the part people underestimate. It has to hold when two shoppers hit the same last unit through two different bundles in the same second. The app calculates the quantity to deduct for each component in a bundle purchase and fires the decrement directly to the Shopify Inventory API, so stock levels stay accurate against what is actually left, whichever bundle type pulled from that SKU.

02
Surviving quarterly API migrations without merchants noticing
The mistake almost every app makes is scattering API calls through the codebase with the version string embedded at each call site. When the version sunsets, the migration becomes an archaeology project across dozens of files, done under a hard deadline, with no way to test the new version without breaking the old one.
Our approach is to keep the platform at the edge of the application. Every Shopify API call goes through a wrapper class rather than being called directly from business logic, so the version in use is a configuration value set per environment, not something baked into individual call sites. That means we can point a staging environment at the new version, run against it, and migrate whatever call sites actually changed while production keeps running the current version untouched. The domain code on top never has to know the difference.
A real breaking change, caught early
An API version stopped sending location_id on orders, which we had relied on directly. The fix was not a workaround at the call site. It meant fetching the fulfillment location through the Fulfillment API instead and deriving the location from there.
Because the version was isolated to one layer, that was a contained change inside the wrapper rather than a hunt through every place location_id was used.

03
Webhook handling that does not lose events
Shopify sends webhooks for orders, product changes, app uninstalls and mandatory compliance requests. They arrive out of order. They arrive twice. Sometimes they do not arrive at all, and sometimes they all arrive at once because a merchant just bulk-edited four thousand products.
An app that treats a webhook as a simple HTTP handler will lose data. The handler has to acknowledge quickly, do the work in the background, and be safe to run twice on the same event.
Across the six apps we see 4,000 to 5,000 webhooks a day, spiking well beyond that during high-traffic periods like Black Friday. The handler's job is deliberately thin: acknowledge with a 200 immediately on receipt, enqueue the work as a Sidekiq job, and let the queue process it. That separation is what keeps webhook delivery from ever blocking on however long the actual processing takes.
There is no single idempotency mechanism shared across all six apps, because each app's handling of a duplicate webhook depends on what that webhook is doing. On the bundle app, inventory is calculated from order state rather than applied as a delta per event, so a repeated order webhook does not double-reduce stock. Other apps handle their own webhook-triggered actions according to what they do, rather than relying on one universal dedup rule.
Failed jobs fall back to Sidekiq's standard retry behaviour, so a failure is retried automatically rather than silently dropped. The mandatory GDPR compliance webhooks are handled directly: on customers/redact and shop/redact, the shop's data is actually removed rather than flagged or anonymized.

04
Logic split across two runtimes
The bundle app uses cart transforms and a checkout extension, which means part of the pricing logic runs inside Shopify's own infrastructure rather than in our Rails app. Splitting logic across two runtimes, where one of them you cannot debug directly, is its own discipline.
Built for Shopify is assessed on real storefront performance, not on a Lighthouse score in your own browser. The bundle widget renders inside a merchant's theme, on their hosting, alongside every other app they have installed, on a shopper's phone.
how it runs
We work in your existing codebase, in your existing repository, behind your existing tests. Value arrives from the first month rather than at the end.
01
A workshop first
Half a day with the people who use the product daily. They will tell you where the manual work is faster than any audit will.
02
A prioritised list you approve
Every item with what it does, what it costs and what it saves. You pick the order, and you can stop after any item.
03
Shipped in slices
A live URL every week. Each improvement goes out on its own rather than waiting for a big release.
04
Your team keeps working
We fit around your roadmap rather than freezing it. Nothing we do blocks a release you already planned.
what this means if you are hiring us
Three things, and they are the reason this page exists
We publish this because it is the only work we can show you in full detail, including the parts a client NDA would normally hide.
We maintain software on a schedule we do not control
When we sell you a Rails care plan, we are not describing a service invented for a pricing page. It is what we do to our own products every quarter, on a deadline, with our own revenue on the line if we get it wrong.
We know what a platform upgrade actually costs
We have paid it more than twenty times. That is why our Rails upgrade advice is one minor version at a time with the suite green at every stop. It is not a philosophy. It is what we learned by doing the alternative once.
We have run production systems long enough to be pessimistic in useful ways
Thin handlers, retries, per-tenant queues, and a plan for the webhook that arrives twice. These are habits from operating our own software, not chapters from a book.
where to start
It pays for itself fastest and it needs no design decisions or model choices. Find the three things your team does by hand every week and remove them. The saved hours fund the rest of the programme, and it proves the process before anyone commits to a larger budget.
Then intelligence or interface, depending on where the pressure is. Internal efficiency points to intelligence. How you look next to a competitor points to interface.
Apps live in the Shopify App Store
6
Active merchants across all apps
895+
Oldest app launched
26 May 2020
API versions migrated since launch
20+
Webhooks processed daily
4,000 to 5,000
Built for Shopify certified
AI Product Bundle Builder
Rating, AI Product Bundle Builder
4.7★ (21 reviews)
Languages supported
English, French, German
Stack
Rails, Shopify Functions, Sidekiq











