We built the app around one rule: AI output is a draft, and a draft only reaches the store after a merchant accepts it and a snapshot of the original has been saved.
Key design decisions
- Catalog audit in one pass. A Shopify bulk operation exports the whole catalog; every product gets a 0–100 score and a list of gaps (missing or short description, missing or overlong meta tags, images without alt text, overlong title).
- Dry run by default. Generation writes
Draft rows (one per field, or per image for alt text) with the original and the proposal side by side. Nothing is sent to the Admin API until the merchant accepts, edits or rejects each field.
- Snapshot before every write. Applying a product first stores its current title, description, SEO fields and alt texts in a
Snapshot that expires after 30 days, then calls productUpdate. A retried apply reuses the unrestored snapshot, so originals are never overwritten by AI values.
- Safe rollback order. Rolling back a job, or one product, restores the snapshot, but only if no newer applied job changed the same fields. Otherwise the app asks the merchant to roll back the newer job first, instead of silently discarding it.
- Limits enforced twice. The response schema carries
maxLength per field (meta title 60, meta description 155 by default), and the output is truncated at a word boundary afterwards. HTML descriptions are sanitized against an allowlist and trimmed by whole blocks.
- Facts only, data as data. The prompt allows only facts present in the product data, and the product data is wrapped as untrusted input. Banned and required keywords produce warnings on the draft.
- Alt text through
fileUpdate. A vision model (gemini-2.5-pro) looks at each image; alt text is written with fileUpdate on the MediaImage, because productUpdateMedia is deprecated.
- Resumable background jobs. BullMQ workers process scan, generate and commit queues in batches with per-shop and global AI rate limits in Redis. Items left mid-flight after a crash return to pending, and plan credits are checked inside the worker.
Architecture
An embedded Shopify app on React Router 7 with Polaris web components, Postgres through Prisma (Job → JobItem → Draft, plus Snapshot, ProductAudit, Preset and SyncEvent) and a separate BullMQ worker on Redis. Every Admin GraphQL operation (Admin API 2026-10) lives in one module with throttling retries and userErrors checks. A products/create webhook can queue new products for review or apply them automatically, deduplicated per product and update time. Shared packages used across DEV3's Shopify apps provide the Google Gemini API client, queues, rate limiting, billing and Shopify helpers. GDPR webhooks delete a shop's data on redaction.