The single most common architecture mistake I see in Shopify development is choosing apps where Functions should be used, or trying to use Functions where an app is required. They solve fundamentally different problems.
What Shopify Functions are
Functions are WebAssembly modules that run inside Shopify's infrastructure at checkout time. They execute in under 5ms, with no external network calls. They can modify discount calculations, validate carts, filter payment methods, and customize shipping rates — directly in the checkout pipeline.
What apps are
Apps are external services — typically a Remix or Node.js server you host — that communicate with Shopify via the Admin API and Storefront API. They can do almost anything: read and write any data, display UI in the admin, create webhooks, handle background jobs.
The key difference: where they run
- →Functions: inside Shopify's checkout pipeline, synchronous, sub-5ms, no external calls
- →Apps: on your infrastructure, async-capable, can call external APIs, full network access
If you need to modify checkout behaviour based on cart contents, use Functions. If you need to sync data to an external system, use an app with webhooks.
Functions: what they can do
- →Custom discount logic (percentage, fixed, BXGY, tiered, volume)
- →Payment method visibility rules (hide/show methods based on cart, customer, address)
- →Shipping rate creation and filtering
- →Cart and checkout validation (block checkout if conditions aren't met)
- →Cart line attribute injection
Functions: what they can't do
- →Call external APIs (no network access)
- →Read order history or customer purchase data beyond current session
- →Write to any Shopify data (read-only access to checkout context)
- →Run for more than 5ms
Apps: when you need them instead
Use an app when you need persistence (storing data), external API calls (ERP sync, CRM update), background processing (order fulfillment logic), or admin UI (a custom dashboard your team uses).
// Functions input: read-only checkout context
export function run(input: FunctionRunResult): FunctionRunResult {
const { cart, buyerIdentity } = input;
// B2B volume discount: 10% off orders over £500
if (cart.cost.subtotalAmount.amount > 500) {
return {
discounts: [{
value: { percentage: { value: "10.0" } },
message: "Volume discount: 10% off orders over £500"
}]
};
}
return { discounts: [] };
}Building something like this on Shopify?
I build exactly what I write about. If you need help implementing this, get in touch — I respond within 24 hours.
Get new Shopify guides by email
Practical, technical articles on Shopify development — no fluff, no sales emails.