Field Study 01

What happens when the app you depend on disappears

A specialty retailer running 12,000 SKUs lost the third-party app that managed their entire purchasing and inventory pipeline. Instead of finding a replacement, we built a system they own. Ownership changed everything.

The problem is not the app. The problem is that you do not own it.

A specialty e-commerce retailer with an international customer base and roughly 12,000 active SKUs had been running their purchasing and inventory operations through a third-party Shopify app for years. The app handled purchase orders, receiving, and stock tracking.

It worked well enough. But “well enough” deserves scrutiny.

The app was capable of far more than the team ever used. It had modules for demand forecasting, supplier analytics, reporting dashboards, automated reorder points. None of that was ever turned on. The team used it for one thing: adjusting inventory quantities when a shipment arrived. Maybe 25% of what they were paying for. The other 75% sat untouched, not because the features were bad, but because they were built for a generalized version of the problem that had nothing to do with how this particular business actually worked.

This is the norm, not the exception. The e-commerce app ecosystem is overwhelmingly overwrought. Tools ship with dozens of features because they serve thousands of stores, and no two of those stores need the same subset. The result is bloat: businesses pay for platforms they use minimally, build their workflows around the 20% that applies, and quietly ignore the rest. The real value the tool brings is buried under features nobody asked for and configuration screens nobody opens.

Then the app was deprecated.

No migration path. No export that preserved the relationships between suppliers, purchase orders, and inventory movements. Just a sunset date and a suggestion to find something else.

This pattern shows up constantly and almost never gets discussed until the damage is done. A business builds its operations on top of an app it does not own, does not control, and cannot modify. The app works fine for three years. Staff build muscle memory around it. Workarounds become workflows. Then the app changes its pricing, or its API, or its priorities. Or it simply stops existing. And the business discovers that what felt like infrastructure was actually someone else’s product decision.

The retailer had a choice: find another app and repeat the cycle, or build something they actually own. They chose to build.

What “build” actually meant

The scope was not “replicate the old app.” The old app had done a reasonable job of the basics (creating purchase orders, tracking what arrived) but it had never solved the harder problems because it could not. It was a general-purpose tool serving thousands of stores. It had no knowledge of this retailer’s supplier relationships, their receiving workflow, or their reporting obligations.

Building a custom system meant solving the problems the app had been silently ignoring for years.

We designed three interconnected modules: a supply chain backend for purchasing and inventory, a packing slip parser for receiving, and an event-sourced preorder pipeline for reporting. Each module does its own job. Each one also emits information to the others. Together they form an ecosystem. This is something a standalone app almost never becomes, because apps are built to be installed, not integrated.

Purchasing: the decisions that shaped it

The supply chain backend handles purchase orders from creation through receiving to inventory adjustment. That sounds simple. It is not.

A purchase order in e-commerce is not a document. It is a relationship between a supplier, a set of products, a set of expected quantities, a destination, a timeline, and a Shopify location. When a PO is received, it is not a single event. It is a series of inventory mutations that must be written to Shopify’s API individually, each with an idempotency key, because the same shipment will sometimes be scanned twice by different staff members on different days. If those writes are not idempotent, inventory counts drift. Once they drift, they never come back without a manual audit.

We built the backend in FastAPI with a React admin dashboard. Every inventory write goes through Shopify’s GraphQL API. Every mutation is logged. If a receiving session is interrupted (the browser crashes, the network drops, a staff member walks away) the session can be resumed from exactly where it stopped. Nothing is lost because nothing is held only in memory.

The vendor mapping layer was another decision that shaped the whole system. In publishing and specialty retail, the company you order from is often not the company that manufactures the product. Distributors carry products from dozens of manufacturers. A single purchase order might contain products from five different makers, all ordered through one distributor. The system needed to model this: ordering parties, distribution clients, imprints, and the specific codes that each supplier uses for routing. We call this the vendor cosmology. It is a map of who makes what, who distributes whom, and how orders actually route through the supply chain. No off-the-shelf app models this, because most apps assume one supplier equals one brand.

Receiving: where the real cost was hiding

Before the system existed, receiving a shipment worked like this. A box arrives with a packing slip. A staff member reads the slip line by line, finds each product in Shopify, types the quantity, and adjusts the inventory. A typical shipment has 30 to 60 line items. The process takes 15 to 20 minutes per shipment, multiple times per week.

But the time cost was only part of the problem. A packing slip is not just a list of what arrived. Buried in the line items and footer notes are pricing changes, backorder notices, out-of-stock flags, and substitutions. A human scanning a slip for quantities will miss these signals routinely. Not because they are careless, but because they are focused on the primary task. The information is there. It is not surfaced. It never enters any system. It lives and dies on a piece of paper that gets filed in a box and never looked at again.

We built a parser that uses a vision model to read a photograph of the packing slip. The model extracts every line item (SKU, description, quantity, unit cost) and pre-fills the receiving wizard. Staff review the extracted data, confirm or adjust, and submit. Inventory adjustments are written to Shopify automatically.

The parser does not just read quantities. It reads the full context of the slip: flagged items, cost discrepancies against what was ordered, notes about backordered products. These are surfaced to staff as exceptions before any inventory write occurs. A pricing change that would otherwise be discovered weeks later during a margin review is caught at the moment of receipt.

Receiving time dropped from 20 minutes to under 5. More importantly, the information that was always on the slip but never made it into any system now enters the ecosystem at the point of origin.

Cauldron·OS
Packing slip parser · Live demo

Step 1 — Upload slip

Photograph or drop packing slipJPG, PNG, or PDF · one page per upload

Step 2 — Vision model reads

Waiting for upload
Upload a packing slip to begin.
Shopify GraphQL API
Extracted line items
— lines—
SKU
Product
Qty
Cost
PO match
Shopify
—
—

Preorders and reporting: why event-sourcing matters

An order in Shopify is an order. There is no native concept of a preorder, a delayed shipment, a release-day hold, or a special on-sale date. Shopify gives you orders, and it gives you tags.

Tags can only do so much. Manual tagging is error-prone and does not scale. Automated tagging (through Shopify Flow or a third-party rule engine) helps, but it is brittle: tag logic sprawls, edge cases multiply, and nobody can reconstruct why a particular order was tagged a particular way six months later. Meanwhile, orders with special status get lost in the queue. A preorder for a product that does not release for three months sits in the same “unfulfilled” list as an order that should have shipped yesterday. Staff learn to visually skip certain orders. Inevitably, they skip the wrong ones.

This is not just an operational headache. It is operational risk. A busy store processing hundreds of orders per week is making daily decisions about what to ship, what to hold, and what to flag, all based on a system that treats every order identically and relies on human judgment to sort out the exceptions. The busier the store gets, the more exceptions there are, and the less time anyone has to manage them.

The retailer has reporting obligations that require accurate, auditable records of presale commitments. Before the system existed, this reporting was assembled manually. A weekly process that took hours and was perpetually at risk of human error.

The preorder pipeline is event-sourced. Every relevant Shopify event (order created, paid, fulfilled, refunded, inventory updated) is captured as a raw webhook payload and stored permanently. A classification engine processes each event and determines the preorder status of every product: active, historical, or one of several anomaly states.

A commitment ledger is reconstructed from the event log. Lifecycle snapshots freeze presale totals when a publication date passes. A weekly release engine produces the final reporting dataset.

The critical design decision: the entire pipeline is replayable. If any derived table is corrupted, or if the business changes a reporting policy, the system can rebuild from the raw event log deterministically. Nothing is thrown away. Nothing is approximated.

This is where the ecosystem design proves its value. The preorder module does not just report on its own data. It receives signals from the purchasing module. When a PO is created for a preorder title, when that PO is received, when inventory is allocated. These signals enrich the reporting with real operational context. A report that says “44 preorders committed” is useful. A report that also says “PO submitted to supplier, expected July 5, 80 units ordered against 44 commitments” is actionable. It reflects real business policies, not just raw counts.

Apps do not do this. Apps do not talk to each other with this level of specificity. Modules in a custom system do, because they were designed together.

What changed

The quantitative outcomes are real but they are not the point.

Receiving time went from 20 minutes per shipment to under 5. Inventory drift (the gap between what Shopify says is on the shelf and what is actually on the shelf) went to zero and has stayed there. Reporting that once took hours of manual assembly now runs automatically. Bandwidth usage on the reporting pipeline was optimized from tens of gigabytes per month to under one, by redesigning workers to avoid scanning full tables.

The qualitative change is harder to measure and more important. Staff trust the system. They open it because it tells them something useful, not because they have to. The dashboard was designed to be calm. Not because calm is a style preference, but because a tool that someone stares at for hours should not be an assault. Data is clearer when the surface presenting it has been considered as carefully as the data model behind it.

And the business owns it. When Shopify releases a breaking API change (and they do, regularly) the system is updated in days, by someone who understands it, because the codebase is documented, version-controlled, and designed to be maintained by people who did not write it. When the business changes a policy (a new reporting threshold, a new supplier relationship, a new location) the system adapts, because it was built around their operations, not around a product manager’s roadmap.

The supply chain system has been in production for over a year. It has processed thousands of purchase orders, tens of thousands of inventory mutations, and every weekly reporting cycle since it launched. Nothing has been lost. Nothing has drifted.

Explore the interactive demo →

The real argument

The case for building a custom system is not “apps are bad.” Apps are fine for problems that are genuinely generic. Payment processing is generic. Email delivery is generic. Nobody should build those.

The case is narrower and more specific: the operational core of a business (how it purchases, receives, counts, and reports on its inventory) is not generic. It is particular to that business, shaped by its suppliers, its staff, its obligations, and its history. Wrapping that particularity in a general-purpose app works until it does not. And when it stops working, the cost is not just the app. It is every workflow, every workaround, and every piece of institutional knowledge that was built on top of it.

A system you own is a system that can grow with you. A system you rent is a system that can disappear.

This is a Field Study — a composite account drawn from patterns observed repeatedly across engagements. Specific numbers and scenarios are illustrative, not drawn from a single client. The pattern itself is real and nearly universal at this size.

More patterns

All Field Studies →

Recognize this in your stack?

Book a call →