The brand runs its own sewing workshop, keeps an archive of three hundred plus prints, and holds every piece of product data inside a major e-commerce marketplace account. The store is built so the catalog never has to be maintained twice: 64 models and 307 colorways arrive from the seller account on their own, prices recompute three times a day under the owner's rule, and anything staff edited by hand is never overwritten again. Storefront, admin panel, orders, Telegram alerts. Payments are a separate stage.
The brand sells through a major e-commerce marketplace. Every product card lives there: titles, prices, attributes, photos. A direct store is not a replacement for the platform, it sits next to it: direct sales without commission, own terms, own face. The owner set the hard constraint himself: he will not maintain the catalog in two places. Which means everything already described in the seller account has to be pulled in by the site.
Three hundred colorways, each with a name, a price, a size run and a dozen frames. Entering that by hand takes weeks and goes stale the same day the owner reprices on the platform.
Prices move with campaigns and repricing, sometimes several times a week. A store with last season's price list is worse than no store: the buyer compares and leaves.
The platform returns one card per color. Showing them as they come means dumping three hundred nearly identical tees on the buyer instead of sixty four models.
No developer in the loop: descriptions, prices, frame order, home page blocks, order statuses. And in a way that the nightly sync will not wipe out.
Stage 1: site, admin panel, first catalog load. Stage 2: marketplace sync. Stage 3: payments, shipping, courier integration. This case covers the first two: they are done and running in production. Payments are deliberately not wired yet, an order is placed without a charge.
One application, Next.js on the App Router. Everything that talks to an external system lives in its own module and goes out on a schedule only: the storefront and the admin panel read our own database, never someone else's API. That way a page does not depend on whether the marketplace answers right now.
Catalog order is driven by order count over the last thirty days. The obvious move is to ask the platform for a month at once. You must not, and that did not come from the docs, it came from the live API. Each of the three findings broke the catalog silently for a month first.
The platform does say how long to wait, in a header of its own, while the standard header for exactly this is missing from the response entirely. As long as only the standard one was read, the pause had to be guessed on a ladder: a minute, five, twenty. Every attempt landed inside an already declared ban and confirmed it.
Each run takes the day that is missing from history entirely, or failing that the least recently refreshed one. The run assumes it will get exactly one request through, and spends it on the most useful day.
Days accumulate in our own table and outlive the window. A refusal on one day does not zero out a product: the day keeps its previous record, and the run journal states which days did not come through.
A pause longer than five minutes is not waited out inside the run: the deadline goes into settings, the run ends, and the next scheduled one simply will not start until the time is up. A successful request clears the ban.
The platform applies different quotas to the same endpoint depending on token type: some tokens get a request per minute, the basic one gets one request per three hours. Ours turned out to be basic, which explains everything else: one successful request, then ninety minutes of refusals. This is not overload and not third-party services, it is a published limit. The owner got it as a single action: complete account verification or reissue the token, and order history fills in over days instead of a month.
The rate-limit pause, the token scopes and expiry, the number of runs in the last day and a journal of the last twelve runs sit on their own admin page. Without that, a throttled account reads as a broken site or an expired token, and the owner calls the developer instead of waiting it out.
The owner wanted something simple: cheaper on the site than on the platform. That reads as unambiguous right up to the moment you ask which price the buyer actually sees on the marketplace listing.
It can compute from the seller price or from the marketplace listing price. The base is chosen by the owner rather than hardcoded, which turns "cheaper than on the marketplace" into a checkable promise.
The value in code is a fallback for an empty database only. The owner sets the rule in the admin panel, and all three runs read it. If even one computed from code, sync would revert prices to the old percentage and the edit would undo itself.
Parsing silently swaps a broken value for the fallback. A nightly run must not stall over a typo in one number, otherwise prices fall a day behind the platform.
While popularity was computed inside a successful price run, every rate-limit refusal left the catalog in yesterday's order, even though the platform was serving orders perfectly. They are two independent commands now.
The marketplace returns a separate card per color. The key that ties colors of one garment together arrives as its own field: cards are folded into a model by it and stay colorways inside, behind a switcher. Without that grouping the storefront would read as three hundred nearly identical tees.
| Decision | How | Why |
|---|---|---|
| Colorway name | taken from the seller article code, the attribute is the fallback | In the Color attribute the platform lists every color present in the print: a tee with lemons carries four values. That describes the picture, not the variant. |
| Product title | word for word as on the platform | A buyer arriving from the marketplace has to recognise the item by name. A shortened form is needed in exactly one place, the page URL. |
| Colorway order | ascending by card id | The platform returns cards in update order. Without explicit sorting the colors would reshuffle on the storefront after every sync. |
| Photos | copied to our storage, the platform URL stays the matching key | Swapping the URL for a local path is not an option: the next run would treat every frame as gone from the card and delete them along with the ordering and the hide flags. |
| Frame ordering | sync touches the set, never the order | Order and hidden frames were set by staff. The order used to be rewritten by every run, and the arrangement reverted to the platform's own within a night. |
| Zero-stock size | stays on the page, struck through | A size that disappears reads as "not for me". A struck-through one says the garment exists in that size and may come back. |
Staff edits a title or a description, the field gets a lock, and sync stops rewriting it. This is what keeps the admin panel from being decorative: without locks any manual edit would live until the next night, and the owner would stop editing altogether.
The brand has its own production and an archive of three hundred plus prints, so the site is built as a swatch catalog rather than a lookbook: white paper, press blue as the only accent, zero corner radius. Three typefaces split the work between brand voice, body text and data. Article codes, sizes, fabric weight and print names are set in monospace, which is exactly how a swatch catalog reads. The signature element is the print ticker under the first screen: colorway names scroll past and state the scale of the archive in names rather than in a number.
Sections differ by role, and access is decided by each page itself. That is not the only line of defence: server actions check the permission a second time, because an action is callable directly over HTTP and the shell check never runs in that case.
What needs action first: colorways without a price, new orders, sizes at zero stock. General numbers after that, and only the ones the role may see: revenue is the owner's money, not a public stat.
List, detail, status changes along permitted transitions only. Contact-form enquiries sit in the same section: they carry personal data and the same person answers them.
Search, title and description editing, prices per colorway, stock per size, hiding frames, and field locks that keep sync from overwriting the work.
A frame around the page, text edited in place, save and undo buttons. Undo works off a journal of previous values rather than off tab memory.
Home page blocks live in the database. The page does not know what it is made of: it reads the blocks in order and fetches in one pass whatever they need from the catalog.
An order lands in the work chat over a webhook. Queue, reminders and cleanup run as a separate scheduled command every ten minutes, so an unreachable messenger never breaks checkout.
The list offers the status as a select right in the row: there is no need to open a card for a single transition. The card holds what people actually open it for: the lines, delivery, payment and history.
The home page, pages, documents and social links sit under a single menu item. They used to be four separate rows and the menu fell apart.
Two counters, a funnel and goals. A product view counts once per visit, not twice: the event hangs on the card, not on every render.
Checkout no longer ends with an email to a manager. The customer picks a delivery service and a pickup point on a map right at checkout, pays by card or by instant transfer, and the operator prints the waybill from the admin and watches statuses that arrive on their own.
Payment by card and by instant transfer through a QR code. Bank notifications hit our own endpoint, the signature is verified, and an order changes state only on a confirmed notification, never on the customer returning to the site.
A button on the order card returns the money by the same method it came in. The payment status never moves backwards: it moves forward into the refunded state. A customer can cancel a fresh order themselves and the money goes back without an operator.
A courier network with pickup points on a map and a waybill from the admin, the national post with a parcel created from the office index and its own label, and an on-demand service with a request and polled statuses.
One service has webhooks, another has scheduled polling: whatever each one offers. The operator does not walk around carrier portals, they see the tracking number and the state in the order row.
If a service refused to print a label, the operator sees its wording, not a bare error code. Otherwise the investigation turns into a support thread while the order waits.
A checkbox on sign-up and at checkout, stored together with the order. It is not decoration: without it you may neither write to the customer nor hand their address to a carrier.
One module, one folder, its own README, its own tests next to the code. The module boundary is its index file: importing another module's internals is not allowed. If you need to, the public API is missing something, and that is where it goes.
Not what the code does, that is visible from the code, but which decisions were taken and why. Half the findings in the sync section live there, so the next person does not step on them again.
The rules file and its test file share a folder. You can see what is covered and what is not, and the test travels with the code. Tests never touch the network: the HTTP client takes its fetch from outside.
The harness swaps the database URL before the client is created and fails if the URL does not carry the test name. Without that check one bad run would wipe the development catalog.
Tests, types and the build run on a separate copy of the project with a separate database. A local database is no longer needed, and the production shop is never touched.
A deploy script, an auto-renewing certificate, redirects from www and from the raw server address to the canonical domain. The run schedule lives in the system scheduler, not inside a web request.
It is what happens when pure rules are separated from database and network work. Card mapping, price computation, order status transitions, price-rule parsing, colorway grouping: all of them are side-effect-free functions, and a test for one is cheap. The expensive end-to-end checks stay where nothing else will do.
| What | Before | After |
|---|---|---|
| Catalog | inside the marketplace account only | 307 colorways on the brand's own site, refreshed automatically |
| Prices | moved on the platform, no direct site | recomputed three times a day under the owner's rule |
| Product order | - | by 30-day order count, history accumulates on its own |
| Catalog editing | through the platform account only | admin panel with field locks, edits survive the night |
| Photos | served by the marketplace CDN | 4,478 frames hosted by the brand |
| Orders | - | checkout, statuses, Telegram notification |
| Platform limits | refusals looked like a broken site | explained on screen in words, runs wait on their own |
The store runs in production on its own domain, with a certificate and auto-renewal. Catalog, prices and product order refresh on a schedule with no human in the loop. The owner edits the storefront and the orders himself. The next stage, payments and shipping, is split off deliberately and scoped as a separate assignment.
Business analysis and automation: ERP systems, dashboards, marketplace integrations and AI agents. Former COO of a company doing 4 million dollars a month, now building these systems for clients.