CrushSuite · Client data dashboard · Shipped 2026
Product, design and engineering. One builder, end to end.
I designed, built and shipped a new analytics dashboard for CrushSuite on my own: product, design and engineering, end to end. This is how I decided what to build, the system I built it with, and how I kept one owner from becoming one point of failure.
Wineries don't have the data to understand their customers. We do.
When a winery sells a bottle on Shopify, the order goes to two more companies. A compliance partner checks that it's legal to ship to that state. A fulfillment partner packs it and ships it. Each company sees only its own step, and the winery never sees them together.
CrushSuite connects all of them. Our Compliance app runs inside the Shopify checkout, and our Clubs app runs the wine club. So every order passes through us at every step, across 50+ wineries.
That data was sitting unused. The product was simple: show each winery what we can already see.
We could see what a winery couldn't: which wines people come for, and which they add.
The Marigny has a club of about 800 members. Online, one of its white wines wasn't selling, so they stopped printing its labels.
In the next club release, that white was the number one optional add-on, and it sold out.
The data showed why. Nobody went online to buy that white. They went online for a red. In the club, members were already buying, so adding the white was easy, and at that price in a warm month it was the natural add.
It was never a bad wine. It was a wine people add, not one they go looking for. Reading online sales alone, a winery cuts it. We could see both sides.
I turned stopped orders into answers wineries look up themselves.
When an order stopped, the winery only saw that it didn't go through. So they emailed whoever they could think of: us, Shopify, their compliance partner, their warehouse. Hours went into finding out whose problem it was.
Every order passes through us, so we can see why it stopped. I built a responsibility matrix: for each reason an order stops, who caused it, who fixes it, and how.
| Why it stopped | Who caused it | Who fixes it | How |
|---|---|---|---|
| Card declined or no payment method | The customer | The customer | A working card, through Shopify payments |
| Out of stock | The fulfillment partner or the winery | The winery | Restock at the warehouse, or swap the wine |
| State shipping or volume rule | A compliance partner rule | The winery | Adjust the order to fit the state's rule |
| Setup or sync error | CrushSuite | CrushSuite | We fix it. The winery does nothing. |
I put the matrix in our knowledge base, so every winery can look up a stopped order before they email anyone.
Then I automated it. When a stopped order comes in with a reason the knowledge base doesn't cover yet, it gets flagged, we solve it once, and the answer goes into the knowledge base. We handle each new issue one time. Repeat issues answer themselves.
That did two things:
- Less back-and-forth. Wineries check the matrix before they reach out.
- A product we never meant to sell. It became one of the reasons wineries value us.
And because we see 50+ wineries at once, when the same reason shows up everywhere, we know the cause is upstream, not at the winery.
What I built: an analytics dashboard that reads every CrushSuite order and billing event, every night, and turns it into answers per winery. I designed it, built it and shipped it to production myself.
What I decided before writing code, and what I chose not to build.
- Production is read-only, forever. One read-only function is the only way in.
- Code computes every number. Claude writes only words, and any text it writes that contains a digit is rejected.
- No real number on a screen before a money spike matches Shopify.
- $20 a month is the only allowed cost. Anything that adds a fee stops the work and comes to me.
- Six slices, v1 first. A slice starts only when the last one's numbers reconcile.
I proved it on a small sample before trusting the whole. I picked four wineries that, together, hit every case that breaks billing math:
- Both apps installed
- More than one club
- A club migrated from another platform
- A plan change or a frozen store
- A Compliance-only store with high order volume
I worked out their numbers by hand first. The code had to match all four to the cent before it ran on the rest.
Not built yet, on purpose: a health score, money at risk and an MRR bridge. Those are judgments about money, and they wait for two months of money that reconciles.
How one person shipped product, design and engineering: an agent team, with gates I set.
Design started on a canvas on Oct 2. Code ran Oct 7 to 9, about 168 commits, and real data was on the screens by Oct 9.
A principal agent plans and six specialists build: backend, frontend, database, code review, QA, and one that keeps the docs true. I approve every plan and every merge. Every task runs plan, failing tests, code, checks, and I send back any plan that adds a dependency or skips tests.
I proved the gate before I trusted it. A deliberately failing test went in, CI turned the first PR red, and the revert turned it green. Then I cut the suite from 457 tests to 62 that guard what can actually be wrong: money rounding, totals that add up to their parts, no real winery names in sample data. It has grown since, as real data landed.
How I kept one owner from becoming one point of failure.
Our lead engineer owns production. I gave him the controls.
- A read-only role with hard limits. The dashboard reads production through one role, dashboard_ro: a 15 second statement timeout, a 1 second lock timeout, two connections, and no grants on customer names, emails or addresses. A bug on my side can't load his databases.
- His review. Twenty minutes of his time on the PR before it touched production. Anything on his paths (the production reader, the money formulas, the nightly sync) waits for his comment.
- Work sequenced so each piece ran in isolation. The money spike was scripts with no UI and no production, the screens ran on sample data, and real data came in one slice at a time. His databases come last.
- His stop button. One command, ALTER ROLE dashboard_ro NOLOGIN, cuts the dashboard off, no deploy needed. Secrets never go into a chat or the repo, and test data is anonymized.
What I cut when the numbers didn't reconcile.
The plan was to derive monthly recurring revenue per winery from Shopify's app events. Every rule was a guess about money that hadn't arrived: when a trial ends, what an upgrade is worth, whether a closed store still pays. It didn't reconcile cleanly with Shopify.
So I cut it. A plan is a label, and money is only what Shopify's Partner API says came in. No MRR, no trial math. Once I cut it, the numbers matched Shopify to the cent.
What it changed: answers wineries use without us, and numbers that match Shopify to the cent.
Stopped orders went self-serve: wineries check the matrix before they email us, repeat issues answer themselves, and a product we never planned to sell became one of the reasons they value us.
Every number on the dashboard matches the Shopify Partner Dashboard to the cent, and a test fails the build if one moves. It reads 98 stores and about 3,400 billing events every night, across 18 months of history. It runs for $20 a month, and we use what it shows when we sell.
What I owned, and what I didn't.
Mine
- The problem, and what to cut.
- The data rules.
- The design, 14 boards.
- The build: every plan and every merge.
- The risk plan, and what I asked of our lead engineer.
- The responsibility matrix, and putting it in the knowledge base.
- The sales story.
Not mine
- Production, the read-only role and the stop button. Those are our lead engineer's.
- Typing the code. The agent team wrote it, inside the gates above.