The default reflex on Shopify is to search the App Store. Sometimes that’s correct — there are categories where apps are the grown-up answer and custom code would be silly. But the “install an app for everything” approach is also how you end up with a store that feels fast on a developer laptop and sluggish on a phone in Ashburton, with four monthly subscriptions solving problems you had once.
I build and maintain my own store (shop.jimny.co.nz) and I’ve shipped custom Carrier Service work for clients like Great Range Products. The through-line isn’t “always custom” — it’s match the tool to the job. Here’s how I decide.
The case for apps — speed, support, and sanity
Apps win on time-to-live. You install, click through config, and you’re trading money for immediacy. For loyalty points, product reviews, accounting bridges, email marketing — you’re buying an ecosystem, integrations, support tickets, and feature updates when Shopify changes something underneath.
The simplest way to think about it: if the problem is common and mission-critical to many stores, the market has probably produced a decent app. Use it. Don’t build your own review widget unless you have a weird reason.
The case for custom Liquid and theme code
Custom wins when the requirement is narrow and the app market is “almost right” — which sounds harmless until “almost” means staff work around it every day. Custom also wins on ongoing cost: no per-month rent, no third-party script in the critical render path, no surprise UI change on a random Tuesday.
Where this gets interesting is Core Web Vitals. Many apps inject JS globally. Even “small” apps add up. Custom Liquid and careful CSS usually beat a stack of widgets for LCP and INP — not because Liquid is magical, but because you’re not downloading someone else’s entire product for one badge.
There’s also the conflict problem. Apps don’t know about each other’s DOM assumptions. I’ve fixed carts where two “harmless” upsell modules both grabbed the same container and the second one silently failed — which showed up as “sometimes discounts don’t apply” because the failure mode was a half-initialised line item. Custom code can be wrong too, but it’s one brain making one set of assumptions, which is easier to reason about under stress.
Maintenance is real. Shopify moves — section settings, Online Store 2.0 patterns, checkout changes for non-Plus stores, new metafield tooling. Custom work should be written so a human can update it without archaeology. I bias toward boring Liquid, explicit comments where the business rules are weird, and avoiding cleverness that looks impressive in a screenshot but scares the next developer.
Run the same maths on a “cheap” $19/month tool you installed twice by accident — that’s $456/year in duplicate rent before you’ve sold anything extra. I’m not precious about spreadsheets; I’m allergic to paying for the same job twice. If you want a worked example in more detail, the app audit article walks through how those line items creep.
Hidden tradeoffs — both directions
Custom code’s downside is ownership. When Shopify updates theme architecture, sections, or checkout rules, your bespoke bits need attention. There’s no support team — there’s me, or you, or whoever you trust. That’s fine if you budget for it like any other maintenance.
Apps bring hidden coupling: their roadmap, their outages, their approach to GDPR prompts, their idea of “compatible with Dawn”. I’ve debugged more “mystery slowdowns” by removing apps than by micro-optimising Liquid — which is why I point people at an app stack audit and a speed reality check in the same breath as this article.
A decision framework — ask these before you choose
| Question | If “yes” leans… |
|---|---|
| Is this problem basically the same for thousands of stores? | App — let the market amortise development |
| Does the feature touch every page load (global JS)? | Custom or extremely minimal app — performance tax is real |
| Do you need deep integration with your weird internal process? | Custom — “configure” only gets you 80% forever |
| Will staff need hand-holding and a support desk? | App — support is part of what you’re buying |
| Is the monthly fee small and the script lightweight and audited? | App — don’t romanticise building what’s already solved |
Side-by-side — apps vs custom on the dimensions that matter
| App | Custom Liquid / theme work | |
|---|---|---|
| Time to ship | Hours to days | Days to weeks — scoped properly |
| Monthly cost | Ongoing subscription + currency drift | Mostly zero — maintenance as needed |
| Performance risk | Often higher — JS + third parties | Lower if you keep it lean |
| Exact fit | Configurable within their box | As odd as your business requires |
| Risk profile | Vendor changes pricing or features | Shopify platform shifts — you update code |
Examples — where I usually land
Often app: loyalty, reviews, ESP integrations, accounting exports, subscription billing, anything regulated where you want a vendor paper trail.
Often custom: specific cart and line-item rules, bespoke delivery messaging, tight coupling between product tags and fulfilment logic, content layouts that are brand-specific and shouldn’t shift when an app updates, and shipping that looks like the Great Range project — multi-zone physical reality that apps only approximate.
If you’re going to do custom shipping, do it properly — read carrier-calculated vs standard rates first so you’re not paying me to implement the wrong tier of complexity.
Checkout is the awkward middle child. On standard stores you don’t get infinite freedom the way marketing pages pretend; Shopify Functions and checkout UI extensions are powerful but bounded. When someone tells you “we’ll just customise checkout with an app”, ask what surface they actually mean — cart drawer, checkout extensibility, post-purchase, or a hack in theme.js that breaks every upgrade. The honest mapping of tools to surfaces is half the job.
For NZ stores, local expectations matter: clear GST display, shipping that doesn’t look like it was written for Ohio, payment methods people actually use here. Sometimes that’s configuration; sometimes it’s Liquid in the cart and a disciplined approach to copy — not a $60/month “localisation suite” that mostly adds badges.
I still sell merch through shop.jimny.co.nz, which keeps me honest about the boring stuff: variant UX, trust cues, mobile thumb zones. The build choices I make there are the same trade-offs I talk clients through — just with fewer meetings and more excuses when I defer my own tech debt.
Documentation is part of the product. If you commission custom work, ask for a short handover note: what changed, where it lives, what not to touch without testing. Future-you — or the next developer — will thank present-you when Shopify ships a breaking change and someone has to react calmly.
The hybrid approach — boring and effective
The skill isn’t picking a religion. It’s partitioning: apps for commoditised heavy lifting, custom for the bits customers actually notice — the cart, the PDP story, the checkout trust layer, the fulfilment edge cases. That’s usually how I work with fixes and custom projects: keep what’s genuinely good, carve out what’s quietly hurting you.
A pattern I like: use an app for the regulated / high-churn surface (email, reviews, accounting), and own the presentation layer in the theme so your brand doesn’t look like a template wearing someone else’s widget skin. Customers rarely compliment your “tech stack”; they notice when the page feels coherent and when checkout doesn’t throw surprises.
If you’re on the fence, prototype the cheap version first — even if it’s ugly internally — and watch what breaks in real orders. Custom built on a false requirement is expensive. Custom built on a requirement you’ve felt in support tickets is usually worth it.
Performance sits across both choices. If you go custom to remove JS, validate with field data — not just your feelings — using the same approach I outline in the Shopify speed article. Sometimes the right answer is “keep the app, defer its load”, not “rewrite the universe in Liquid”.
Migrations are a natural reset point for this decision — if you’re replatforming, read how I approach Shopify migrations before you reinstall every app you had “just in case”.
A concrete NZ example: rural delivery surcharges are where “almost-right” apps often fail — post codes aren’t the whole story, and checkout needs to be honest before the order is placed. That’s custom-code territory more often than owners expect.