Short write‑ups of problems we hit running a real commerce operation and what we changed in the code because of them. No product announcements; just the reasoning.
One warehouse wanted a fingerprint reader, another wanted to scan a QR with their own phone, the office wanted a PIN on a tablet, and a visiting colleague wanted to clock in wherever they happened to be. How the clocking system grew eight machine types by listening to managers, and the two quiet rules — order the timestamps, find the right employment — that keep the timesheets honest underneath all of them.
Weighted average cost and FIFO computed in one pass over the movement history, a group-level switch that decides which one is "official", and why LIFO is deliberately missing.
No Kubernetes, no managed database, no serverless. aiku runs a multi‑country commerce operation on a handful of rented bare‑metal machines behind Cloudflare — a primary, a replica and a staging box — and we would choose it again. Why metal beats "cloud" for a workload like ours, what the boxes actually do, and the two incidents that are the honest cost of owning the iron.
Shopify first, in 2024; then WooCommerce, TikTok Shop, Magento, Amazon, eBay, Allegro, Wix, and a wholesale marketplace with its own rules. Two years of connecting other people's platforms to our warehouse — the model that survived all of them (platform, channel, portfolio), the lesson about who owns the truth, and the small ugly things that each platform taught us.
We had an attribution feature for years and it had credited 0.08% of customers. Here is what was wrong, what we rebuilt — twenty channels, three email channels, ROAS and CAC with real costs, a per-customer journey — and the one invariant we now check after every change.
Every storefront page sends one small request to our own endpoint — not to a third party. From it: visitors, sessions, page views, country, device, referrer, the traffic source that becomes attribution, registration and add‑to‑basket conversions, "who is on the site right now", product interest per customer, and the list of most‑visited pages that decides what the cache warmer warms. Why a beacon that fires on cached pages was the only design that could work, how bots are filtered, and what we deliberately do not collect.
We replaced a third-party storefront search with our own — Typesense, typo tuning measured against real queries, synonyms shared across fourteen languages, merchandising boosts for staff, a hybrid semantic arm tuned with a harness instead of a hunch — and then turned the queries that found nothing into a shopping list for the buyers. Total AI spend for the whole programme: about six cents.
Fifty‑five kinds of email, one pipeline. How a newsletter to a hundred thousand people is prepared, sent through Amazon SES from a queue, tracked back event by event through SNS webhooks — delivered, opened, clicked, bounced, complained — attributed to the order it caused, and kept from becoming the retry storm that once throttled us at 1,700 calls a second.
Selling across Europe and the UK means every order has to answer one question before a price is final — and the answer depends on two addresses, a postcode, and whether a VAT number is real. How aiku validates tax numbers against VIES and HMRC (with retries, rate limits and an audit trail), and the decision tree that turns a shipping address into a tax category, Canaries and Madeira included.
For years every line we sold carried the same VAT rate, so one rate per order was simply true. Then the range grew to include tea — zero‑rated in one market, reduced in another — and "which rate" became a question per line. The answer was not "read the product's tax code": it was presets instead of codes for staff, tax versioned like price on the historic line, frozen once sold, and a migration that re‑rated the open baskets to the penny.
When the application moved to long‑lived workers, the commercial APM agent we had run for years stopped telling the truth — and on the test runner it stopped the tests. We replaced it with NightOwl, an open‑source telemetry stack for Laravel: an agent on each box, a local buffer that survives outages, and every request, job, query, exception and log line landing as rows in a PostgreSQL database we own, on a server we already pay for, with retention measured in years. The best part arrived by accident — an AI assistant connected straight to that database with read‑only SQL.
Everything that is not a page view runs on Horizon — twenty‑six supervisors across two servers, from "urgent" to "historic backfill". It is the best thing about the system and was the source of its worst mornings: stray workers surviving a deploy and running a query 780,000 times an hour; a supervisor killed but its children kept alive so every queue ran twice; a retry‑after shorter than the job; ten thousand jobs in a queue from a loop that should have been one. What each one taught us, in the order they hurt — and how the beast was finally tamed.
Every storefront page is rendered once on the server — so crawlers and slow phones get full HTML — and once more in the browser, where it becomes an app. How Inertia server‑side rendering runs here (a Bun process under a supervisor, health‑checked on every deploy), what a 418 KB props payload taught us about where the second actually goes, the silent client‑side‑rendering incident, and the three levers we keep on the list.
From the packing bench, one tap books the shipment, prints the label and writes the tracking number onto the delivery note — for eight carrier integrations across four countries, each with its own dialect: SOAP here, REST there, XML that breaks on an ampersand, a service that refuses more than one parcel, cash‑on‑delivery that must go on exactly one box. What the shipment object has in common everywhere, what each carrier taught us, and why every integration ships with a "manual" twin.
Every count, total and trend in aiku is precomputed — by ~650 small "hydrator" jobs that run at the point of change and a nightly sweep that catches what they missed, into ~150 stats tables and 35 partitioned time series at five frequencies. Why we chose that over computing on read, the bug where a single‑day redo flattened a whole month, and the index traps in partitioned tables.
Every deploy was emptying the storefront cache and restarting server-side rendering, even when nothing on the front end had changed. The cause was one string baked into one JavaScript chunk. The fix was a checksum and a meta tag.
The same candle is one thing to the buyer, a box of twelve to the warehouse, and a litre of base to the factory. aiku models that with three corners — the trade unit (the physical thing), the stock (that thing, in this warehouse), the product (that thing, as sold in this shop) — and a number on every edge. Why a fraction is sometimes right and sometimes a bug, and why changing a number on an edge while an order is in flight is the most dangerous edit in the system.
The group catalogue — 62,000 master products feeding 317,000 shop products across 34 open shops — is where a thing is described once and pushed to every shop that sells it, so that in theory one person at one desk can run the lot. Majors and minors for multi‑currency prices, a "follow the master" switch at three levels, the scalar column we are retiring because it silently mixed currencies, and a price cascade that ran for a month multiplying RRPs by pack size before a repair put 1,635 values back.
A card payment arrives three ways at once — the browser says "done", the redirect says "done", and a webhook says "captured" — and only one of them is money. The payments layer we built this summer: server‑verified status, one processing path, locked idempotency, a recoverable state machine, a gateway log for every event, a sweeper, and a playbook that made the next provider a week's work.
Staff access in aiku is granted by job position — "dispatch clerk in the Spanish warehouse", "customer‑service supervisor for the UK shop" — not by ticking permissions. Positions expand to roles, roles to permissions, all scoped to the group, organisation, shop or warehouse they name. Why we chose that, how a permission is checked in the same place for the UI, the API and the MCP server, the "team id" that makes it all silently false if forgotten, and the things we refuse to do with an admin flag.