Shopify POS extensions are where elegant API design meets a queue of humans who do not care about your architecture. I have built POS tiles, modals, line-item actions, post-purchase blocks, custom price flows, date-based discounts, cart checks, and selective sales tooling. The complexity is not drawing a tile. It is designing a workflow that survives a busy Saturday.
The cashier’s clock is the real SLA
A POS feature that takes three extra taps or two seconds of spinner time will be skipped. Associates invent workarounds. Those workarounds become the real system of record, usually in sticky notes and tribal memory.
That changes engineering priorities:
- Prefer defaults over configuration at sale time
- Keep network calls minimal and obvious
- Make failure messages actionable (“retry”, “remove mark”, “call manager”) not stack traces
- Ensure the happy path is obvious on a small screen
Many targets, one mental model
A loyalty or pricing feature often spans:
pos.home.tile.render/ modal for starting work- line-item detail actions for per-line adjustments
- post-purchase actions for after payment
- Admin links for configuring product rules
If those surfaces disagree about what a “mark,” “discount day,” or “custom price” means, you have shipped four apps that share a logo.
The hard design work is the domain vocabulary. Code should follow that vocabulary, not invent parallel names per target.
Offline-ish reality and API truth
POS environments are messy: flaky wifi, device sleep, session expiry, staff switching registers mid-flow. Your extension’s optimistic UI can diverge from Shopify cart state.
Patterns that help:
- Re-read cart state before committing destructive actions
- Make writes idempotent where possible
- Avoid long multi-step modals that cannot be resumed
- Log correlation ids between POS action and backend mutation
Backend companions are part of the POS product
Many POS apps need Lambdas or Remix routes for calculations that should not live only on device, loyalty math, eligibility, audit trails. That backend is not optional glue. It is where policy lives.
Then you inherit all normal API problems: auth between extension and app, versioning, rate limits, and deploy coordination. A POS UI deploy and a Lambda deploy that disagree will fail in front of a customer.
Terraform/CodeBuild pipelines for these apps are not overhead. They are how you keep register-critical software from being “whatever was on someone’s laptop.”
Post-purchase is a different product
After payment, urgency changes. Staff might capture fields, print flows, or attach metadata. The extension targets differ, the patience differs, and the blast radius differs. Do not casually reuse pre-purchase modal flows without revisiting UX.
What I optimize for
When I design POS extensions now, I ask:
- Can a new associate succeed without training slides?
- What happens when the API fails mid-tap?
- Which Admin configuration mistakes create register pain?
- How do we observe failures across stores?
POS engineering is distributed systems plus industrial design. Ignore either half and the floor will teach you.
