Separating Development, Staging, and Production for a Shopify App
Separating environments for a Shopify App usually does not require two organizations. The common approach is to stay in one organization and use different Shopify Apps, different test stores, and different backend resources to carry development, integration testing, and production traffic.
Shopify CLI lets one codebase link to several app configurations, so a named configuration selects the development, staging, or production app. See Manage app config files.
A working environment matrix
| Environment | Shopify App | Store | Backend resources | Purpose |
|---|---|---|---|---|
| Local development | Its own development app | The developer's own dev store | Local services and a development database | Fast iteration and generating test data |
| Staging | Its own staging app | A fixed integration dev store | Separate domain, database, queues, and secrets | Post-merge OAuth, webhook, billing, and regression testing |
| Production | The real app | Real merchant stores | Fully separate production resources | Serving merchants |
A dev store is for development and testing only: it can't be used for production, can't process real transactions through live payment providers, and can't be converted into a production store. See Development stores.
What can be shared
- The same repository and the same core business modules.
- The same organization and its member management.
- Reusable deployment templates, database migrations, and test code.
- Configuration structure that carries no environment-specific values.
What cannot be shared
- A Shopify App's client ID, secret, and released versions.
- The app URL, allowed OAuth redirect URLs, and webhook endpoints.
- Databases, caches, object storage, and job queues.
- Third-party service credentials and their callbacks.
- Logs, alerts, audit records, and data retention scope.
- CI/CD deployment permissions and production approval.
- The test stores used to verify installation.
Separate apps isolate the Shopify platform identity and nothing else — not your database, and not your deploy permissions. The reverse fails too: separate databases behind a single Shopify App do nothing to stop a test configuration from being released to the production app.
Using config files
One codebase can hold several named configurations:
shopify.app.development.toml
shopify.app.staging.toml
shopify.app.production.toml
Shopify's current documentation provides app config link to create or relink a configuration, app config use to switch the local default, and --config to select a target explicitly for a single command:
shopify app dev --config development
shopify app deploy --config staging
shopify app deploy --config production
Shopify does not require every configuration to be committed: you can commit only the production configuration and gitignore personal development ones. Either way, secrets do not belong in a config file — that is general security practice rather than something Shopify's page spells out. Automation must name its target with --config rather than relying on whatever default a developer set locally with app config use.
Naming stores
Choose names that state the product, environment, and purpose for the store's whole lifetime:
product-dev-carl
product-staging
product-qa-billing
product-demo
A personal dev store can be filled with throwaway data at any time; a fixed staging store should hold a reproducible baseline; a demo or review store should stay tidy so it can be recorded and handed to an outside reviewer. Do not name a dev store prod — it invites the assumption that real transactions run through it.
The path from development to production
- Feature branches are verified on a personal dev store.
- After merge, deploy the staging backend and release the matching configuration to the staging app.
- On the fixed test store, verify installation, authorization, scope changes, webhooks, billing, uninstall, and reinstall.
- Keep the evidence and require a human approval for the production release.
- Build the same verified code into a production artifact, inject production configuration, and deploy.
- When releasing the production app configuration, check scopes, redirect URLs, and extension versions again.
shopify app deploy releases the app configuration and extensions that Shopify hosts; it does not deploy your backend. Hosting the backend — building the app, provisioning a database, setting environment variables, running it somewhere — is a separate process; see Deploy to a hosting service. Backend releases and app configuration releases are two separate pipelines, and each needs its own sign-off.
When two organizations are actually warranted
Two organizations are meaningful only when the organization itself is the boundary:
- Test and production belong to different legal entities or owners.
- An outside team may touch test assets but must not join the production organization.
- Compliance requires organization-level permissions, accounting, and auditing to be fully separate.
- The development assets will be handed over to another company as a whole.
Otherwise a second organization adds overhead in members, app ownership, stores, and billing, and still does not replace separating backend resources and credentials.
Before going live
- Every CI command names its Shopify configuration explicitly.
- Staging and production share no domain, database, or secret.
- No dev store has the production app installed, and no real merchant store has a development app installed.
- Redirects, webhooks, and the app home URL all point at the matching environment.
- Production release requires its own approval, and a developer's local default configuration plays no part in it.
- OAuth, scope changes, uninstall and reinstall, and billing have each been run for real on staging.
The point of environment separation is not to have more config files. It is that a failed test cannot contaminate production identity, production data, or a merchant's experience.