CodeLabProsCodeLabPros — homeMenu

Business platforms

An entire business, taken into software

The internal platform a skincare clinic and distribution business runs on: patients, appointments, stock, orders, partners, invoicing and the workflows between them.

Our role
Sole engineer — designed and built all of it
Built with
Next.js · TypeScript · PostgreSQL · Prisma
Where it got to
Built and running
neostrata.co.ke
The public NeoStrata East Africa storefront — the customer-facing side of the business whose internal platform is described here
The business the internal platform was built for
01

The idea

A skincare clinic that also distributes wholesale, run on spreadsheets, WhatsApp and whoever had been there longest. Client history in one place, stock in another, partner orders in a third, and no way to say what the month looked like without somebody spending a day assembling it.

02

What it actually needed

It is two businesses sharing a name. One books appointments, keeps treatment records and sells to the person in the chair; the other sells wholesale to pharmacies and spas, negotiates terms, raises invoices and chases payment. They share products, staff, money and a founder, and very little else. Which meant this could not be a CRM with some inventory bolted on. It had to be one system that both halves could live in. The parts that calcify, access and audit and money, had to be right before anything was built on top of them.

03

What got built

Access and audit, before any features
A login that cannot be left half-finished, a permission model checked on the server for every route instead of hidden in the interface, and a record of every state change with its before and after values. The first phase looked like almost nothing had been built. It is the reason the next seven fit together.
The clinic
Client profiles and a funnel from enquiry to treatment, a weekly calendar, treatment records and skin assessments with clinical notes encrypted at rest, and a plan builder that turns treatments, appointments and take-home products into a quoted plan a client can accept.
Commerce, retail and wholesale in one place
Products carrying both prices, stock held as a movement ledger so it can never change silently, orders through either channel, invoicing, and a queue where finance verifies payment evidence before anything is marked paid.
A portal the partners use themselves
Pharmacies and spas log into their own isolated surface: their catalogue at their prices in their currency, their orders, quotes and invoices, one-click reorder, and a thread to talk to the business. Five currencies, money held as decimals, and every record keeping the currency it was transacted in.
Automation that cannot exceed its owner
Workflows triggered by hand, on a schedule or by something happening in the business. Each one runs as the person who built it and through the same permission checks a human faces, so automation can never reach further than its author.
A growth engine, and then a view of the business
Segments built from rules with live counts, campaigns measured on attributed revenue against their cost, retention cohorts, referrals, and an opportunity view that puts a value on each signal and offers a single action against it.
04

The decisions that shaped it

Build the unglamorous phase first

Access, audit and money were laid before a single business feature existed. Nobody asks for that, and at the end of it the business dashboard was a set of placeholder cards. But permissions, audit and currency are inherited by everything built afterwards, and retrofitting any of them across ninety screens is not a refactor. It is starting again. The cost of doing it first was one phase that looked like nothing. The cost of doing it later would have been all of them.

Write the guardrails before the engine could do anything

The workflow engine is the first part of the platform that acts on its own. It shipped with six limits instead of acquiring them after an incident: a new workflow cannot go live until a dry run succeeds, it is rate limited per day, it cannot trigger itself into a loop, it acts through its owner's own permissions, deleted targets are handled safely, and every planned action is written to the audit log before anything happens. All six were decided while the engine could not yet do a thing.

No machine learning anywhere in it

Every rule, score and decision is ordinary deterministic code. For a system holding clinical records and moving money on behalf of a business that has to answer for both, being able to say exactly why something happened matters more than being clever about it. A decision that can be replayed is one somebody can argue with.

Model multiple locations before there were multiple locations

Adding a second location to a system that assumes one means either inventing opening balances or losing history. Building the dimension in while there was one clinic meant the stock ledger stayed the single source of truth and every existing record could be stamped honestly, not reconstructed.

05

In production

Running on the company's own network, with no cloud services behind it, and every phase written by one person. This is a client system, so what it does is described here and how it is built is not.

06

What this is worth

A whole business can go into software if the order is right. Access and audit went first. Eight phases were built on top of them, and none had to be torn up to make room for the next.

In more detailfor whoever wants it

A clinic and a distribution business are two different companies wearing one name. One of them books appointments, keeps treatment records and sells to the person in the chair. The other sells wholesale to pharmacies and spas, negotiates terms, raises invoices and chases payment. They share products, staff, money and a founder, and almost nothing else.

Before the platform, both ran on spreadsheets, WhatsApp and the memory of whoever had been there longest.

What had to be laid first

Access, audit and money. Not because they are interesting, but because they calcify. Every module built afterwards inherits how permissions work, whether actions are recorded, and what a currency is. Retrofitting any of those across ninety screens is not a refactor. It is a rewrite.

So the first phase was the part nobody asks for: a login that cannot be left half-finished, a permission model checked on the server for every route instead of hidden in the interface, and a record of every state change with before and after values. The business dashboard in that phase was a set of placeholders. It looked like very little had been built, and it was the reason the next seven phases fit together.

Then, in order

A view of the businessA growth engineWorkflows that act on their ownMulti-location, and undoA portal the partners useCommerce and the B2B pipelineThe clinicAccess · audit · moneyBUILT FIRSTINHERITED BY EVERYTHING ABOVE
Each layer can only be as trustworthy as the one beneath it. That is why the phase that looked like nothing had shipped is the one the other seven stand on.
The guardrails, and why they came firstThe workflow engine is the first part of the platform that does things on its own, so it shipped with limits instead of acquiring them. A new workflow cannot go live until it has completed a successful dry run. It is rate limited per day. It cannot trigger itself into a loop. It acts as its owner and through exactly the same permission checks a human faces, so automation can never reach further than the person who built it. Every planned action is written to the audit log before anything happens. None of that was requested. All of it was cheaper to decide while the engine could not yet do anything.

One deliberate absence

There is no machine learning anywhere in it. Every rule, score and decision is ordinary deterministic code. For a system holding clinical records and moving money for a business that has to answer for both, being able to say exactly why something happened matters more than being clever about it. A determination that can be replayed is one somebody can argue with.