The person who sells is the person who stocks, so the admin is a page, not a system
A small storefront where adding stock, browsing the catalog and taking payment all live in one application, because at this size a separate admin system is overhead nobody has time for.
The brief, in specifics
E-commerce & Retail
10 weeks
Fixed price
Client name withheld under NDA. Engagement details are shown to the extent our agreement permits.
What we were brought in to do
A two-person shop selling a small, changing range. We built the storefront, the cart and checkout, and the stockroom page they add products from, all in one application.
A two-person shop with a small, frequently changing range, running on a platform designed for a merchandising team. The catalog was permanently a week out of date, and the cause was arithmetic, not neglect: adding a product took ten minutes in an admin built for someone whose whole job that is, and neither of the two people had ten minutes.
Full-Stack Engineering
Where the old way broke
Small retailers get sold platforms built for large ones, then spend their week in an admin designed for a team that doesn't exist. The real cost isn't license fees. It's that adding a product becomes a job instead of a two-minute task, so the catalog goes stale.
The cost was never the license fee. Every extra field on the product form was a reason to do it later, and later never came, so the storefront showed things the shop no longer had and left out things it did. A separate admin system also meant a sync, and the sync's occasional failures were a class of bug a two-person shop had no capacity to chase.
One application, with the stockroom as a page behind sign-in. Adding a product takes about as much attention as writing a label, and the catalog updates immediately because there's nothing to sync.
The stockroom is a page
Adding a product happens inside the storefront: no second system, no sync, nothing extra to learn.
The stockroom is a route in the same application behind sign-in. There's no separate admin product with its own login, its own vocabulary and its own sync. Adding a product writes to the same database the storefront reads, so the catalog is current the moment the form is submitted. Nothing to publish, nothing to reindex, and nothing to reconcile when it fails.
- A route in the same app, behind sign-in
- Writes to the database the storefront reads
- Nothing to publish, reindex or reconcile
The stockroom: one page behind sign-in in the same application, all seven lines with counts edited in place, two running low, today's orders and edits in one log, and the one table it writes that the shop reads.
Adding a product: name, price, quantity, one photo and a description, with the shop preview beside it and the things taken off the first screen listed as derived, defaulted or added later.
A short add form
A long form is why catalogs go stale. This one takes about as long as writing a label.
The add form asks for name, price, quantity, one photo and a description, and nothing else. Long forms are the reason small catalogs go stale (every optional field is a reason to do it later), so everything that could be derived, defaulted or edited afterward came off the first screen. It takes about as long as writing a shelf label.
- Five fields; everything derivable taken off the first screen
- Long forms are why small catalogs go stale
- About as long as writing a shelf label
Quantity in the cart
The cart, where quantity is changed: a request for three Moss mugs set to the two that exist before payment, the stock check that will run again at payment, and the saved-for-later shelf under it.
Customers change their minds in the cart, so that's where quantity handling lives.
Quantity lives in the cart, because that's where people change their minds: the decision to buy two comes after the decision to buy. Stock is checked at the cart and again at payment, so a quantity that stops being available says so before the card comes out.
- Quantity handled in the cart, where minds actually change
- Stock checked at cart and again at payment
- Unavailability surfaces before the card comes out
Order #1042, paid through the provider and created from its event, independent of the redirect, with the two stock lines it moved on the right and a card number the application never received.
No card data
Payment is handed to Stripe entirely, so the application never sees a card number.
Payment is handed to Stripe's hosted flow entirely, so no card number ever reaches the application, and the compliance surface of the whole project stays where a business this size can carry it. The order is created on the webhook, so a customer who closes the tab after paying still gets their order.
- Hosted Stripe flow: no card data touches the application
- Order created on the webhook, independent of the redirect
- Closing the tab after paying still produces an order
Hosted accounts
Sign-up, confirmation and sign-in are hosted, which is the right trade at this size.
Sign-up, email confirmation, password reset and sign-in are all hosted, which is the right trade at this scale: the failure modes of homemade auth are expensive and the differentiation is zero. The application holds the session and the customer record, and nothing else about identity is its problem.
- Hosted sign-up, confirmation, reset and sign-in
- No hand-rolled auth to get subtly wrong
- The app holds the session and the customer record only
Accounts: sign-up, email confirmation, sign-in and reset all hosted, the customer record and session the application keeps, and what it deliberately doesn't hold.
What we built together
- 01
Kept the stockroom inside the storefront, with no second application to ship
The stockroom is a route in the same application behind sign-in, writing to the database the storefront reads, so there is nothing to publish and nothing to sync.
- 02
Made adding a product a short form, because a long one means the catalog goes stale
The shop added real products during the build, and every field they hesitated over became a candidate for removal. That's how the form reached five.
- 03
Put quantity handling in the cart where customers actually change their minds
Quantity lives in the cart, where people actually change their minds, and stock is checked again at payment so unavailability surfaces before the card.
- 04
Used hosted auth with a confirmation step instead of rolling our own accounts
Hosted auth was a deliberate choice: the failure modes of homemade accounts are expensive and the differentiation is zero.
- 05
Handed payment to Stripe entirely, so no card data touches the application
Handing payment over entirely removed the largest compliance surface the shop would otherwise have carried, for no loss of anything they wanted.
Operational results after launch
1
Applications to run
0
Systems to sync
None
Card data stored
Under a minute
Time to add a product
Applications to run and systems to sync are counts. Card data stored is none, structurally, because payment is a hosted flow. Time to add a product is measured from the shop's own use during the build, from opening the form to the product being live on the storefront.
Client name withheld under NDA. Figures are approximate, drawn from the engagement’s own reporting.
Phase by phase
Phase 1: The Catalogue
Categories And Products
Built browse, category pages and product pages on a small catalog model, sized for a range that changes often, with no pretense of scaling to thousands.
- Catalogue
- Category Pages
- Product Pages
Phase 2: The Stockroom
A Page, Not A System
Built adding and viewing inventory as a page inside the same application, so there's no second deployment, no sync, and nothing separate to learn.
- Add Inventory
- View Inventory
- Stock Levels
Phase 3: Cart And Checkout
Where People Change Their Minds
Built the cart with quantity handling in it, because that's where customers actually adjust, and handed payment to Stripe so no card data reaches the app.
- Cart
- Quantity Picker
- Stripe Checkout
Phase 4: Accounts
Hosted, With A Confirmation Step
Used hosted auth, confirmation step included, instead of rolling accounts by hand. For a two-person shop, that's the difference between shipping and not.
- Sign Up & Confirm
- Sign In
- Session Handling
A product page carrying every variant and its stock: Oat, Moss and Ink, with Moss low at two, and quantity left for the cart.
About our collaboration
This was a two-person shop, and the whole engagement was scoped around that: one application, hosted auth, and payment handed entirely to a provider. Each of those is a deliberate decision about what not to build.
Ten weeks, fixed, with an explicit rule that any field not needed to sell the product stayed off the first screen. The shop added real products during the build, which is how the form got down to five fields: every one they hesitated over was a candidate for removal.
What we'd carry into the next one
Small retailers are sold tooling built for teams they don't have, and pay for it in stale catalogs.
Tooling built for a merchandising team is priced in licenses and paid for in staleness, a cost small retailers absorb without ever tracing it back.
If adding a product takes ten minutes, the catalog will be wrong by the second week.
Ten minutes per product is enough friction to put the task off indefinitely, and a catalog put off for two weeks is a storefront that misrepresents the shop.
A separate admin system means a sync, and a sync means a class of bug nobody has time to chase.
A second system implies a sync, and a sync implies a failure mode someone has to notice. That's exactly the resource a two-person shop lacks.
Handing payment entirely to a provider is the cheapest security decision a small shop can make.
Handing payment entirely to a provider removes the largest compliance surface a small shop would otherwise carry, for no loss of anything they wanted.
One order, cart to stockroom
The shop and its stock can’t disagree when they are the same table.
Follow order #1042 from the cart, through the payment provider, into the stock table and out the other side as the stockroom sees it. Watch the Moss mug trip its low mark, then close the customer’s tab after paying and see that the order still arrives. Switch tabs, or use the arrow keys once one is focused.
Two lines in the cart, each checked against the stock table
Quantity is chosen here, where customers change their minds. Nothing has moved yet: a cart isn't an order.
Illustrative: the shop, the order, the stock counts and the times of day are invented to show the mechanism, and the counts step down slowly so the change is visible. Nothing here is a measurement.
From a quantity in the cart to the count in the stockroom, inside one application
There's no second system in this path. The only thing handed out is payment, and it's handed out entirely.
- 01 · TriggerCart with quantityQuantity is handled in the cart, where customers change their minds, and stock is checked there first.
- 02 · HandoffStripe hosted checkoutStock is checked again at payment, so unavailability surfaces before the card. The card is entered on the hosted page, never in the app.
- 03 · OrderWebhook creates the orderThe order is created on the webhook, so a customer who closes the tab after paying still gets their order.
- 04 · StateOne databaseThe stockroom writes to the database the storefront reads. Nothing to publish, nothing to reindex, nothing to reconcile.
- 05 · DeliveryShop + stockroom routeOne application: the storefront, and the stockroom as a route behind hosted sign-in. The catalogue is current the moment a product is saved.
Right count, no card, one truth
Overselling, card liability & a shop out of step with its stock
Stock is checked twice
Once in the cart and again at payment, so a quantity that stops being available says so before the card comes out.
No card number reaches the app
Payment is Stripe's hosted flow entirely, so the compliance surface stays where a business this size can carry it. The order is created on the webhook, so a closed tab still produces it.
The shop reads what the stockroom writes
The stockroom is a route in the same application, writing to the database the storefront reads. With no second system there is no sync to fail and nothing to reconcile.
Running a small shop from an admin built for a team you don’t have? Scope your build in 3 minutes.
Scope your buildNearby engagements
Data & AnalyticsA checkout that stopped losing sales to a 6-second load
Profiling found the real bottleneck behind a slow checkout (an N+1 query and an oversized bundle) and tuned both, with before-and-after metrics locked in as a baseline against future regressions.
E-commerce & Retail · 5 weeks
Web PlatformsA reader people finish, and a library that remembers where they stopped
A reading platform for a comics catalog: a browse surface people can actually navigate, a reader that gets out of the way, and a history that puts everyone back on the page they left.
E-commerce & Retail · 18 weeks
Web PlatformsOne number, a whole handset, and a database that keeps up with 125 brands
A metered IMEI lookup service that turns fifteen digits into a device, its specifications and its status. It's sold three ways to three audiences and backed by a device database that maintains itself.
Consumer Electronics · 22 weeks
Let's talk
Running a large platform, shaping a first MVP, or getting a product ready for a funding round? Tell us where you are. We'll shape the process around it, and stay with you after launch.














