Most Shopify app tutorials get you to "hello world" installable. That's easy. Making an app that survives 1,000+ merchants is where it gets interesting.
Idempotency isn't optional
Shopify retries webhooks aggressively. If your handler isn't idempotent, you WILL double-charge someone. Every webhook handler needs:
1. A unique key derived from the payload (usually webhook_id) 2. A database row that records "we've processed this" 3. A short-circuit at the top of the handler that returns 200 if already processed
Billing edge cases
The Billing API has three edge cases every app hits:
- Uninstall during trial. The subscription gets cancelled but your DB doesn't know until the uninstall webhook fires. Race conditions are common.
- Store re-installation. Same shop domain, new access token, potentially different plan. Model shops by
shop_id, not domain. - Currency conversion. Merchants can install in any currency Shopify supports. Store amounts in cents in the merchant's currency, convert at display time only.
Polaris is your friend
Custom UI in the Shopify admin is a hostility signal for reviewers. Use Polaris. The design system exists exactly so you don't have to argue with the review team about accessibility.
App Store review
The Shopify review team is thorough. Their most common flags:
1. GDPR webhooks not implemented (three required — data request, redact, and shop redact) 2. Uninstall webhook not cleaning up data 3. Support email that bounces (yes, they test it) 4. Missing pricing on the app listing
Fix these BEFORE submission — the review round-trip is 5–7 days each cycle.
Scaling past 1,000 installs
At 1,000+ installs, three things start to matter more than the app itself:
- Rate limits. Shopify's per-shop rate limit is 40 REST calls/sec (bucket model). Your app has to handle throttling gracefully.
- Webhook queue depth. During flash sales, one merchant can send you 10,000 webhooks in a minute. Queue them (BullMQ, SQS) and process out-of-band.
- Support surface. At scale, 0.1% of merchants email you daily. Build a self-serve support surface into the app — most tickets are "how do I..."
Ship the app small. Instrument it heavily. Then scale.