Kintsugi
A rebuild of the product I work on, from the database up. New data model, new backend, new frontend, same capabilities. It runs in production now.
Kintsugi is the Japanese repair where the crack gets filled with gold instead of hidden. We picked the name before writing any of it, which in hindsight was optimistic of us.
What I found
I joined and spent the first few weeks reading the codebase and using the product. The thing that kept coming up was not a particular bug. It was the product.
- A six year old backend with no structure left in it. Six years of people adding what they needed, which is normal, except that by then nobody could tell you where a given thing lived.
- A relational database being used as a document store. JSON columns holding what should have been tables, the same fact stored in three places, and no constraint anywhere to tell you which of the three was true.
- The standard. Health systems exchange data in FHIR. Ours did not speak it, so certification and integrations both started from a hole we would have to climb out of first.
- A frontend that assumed a desktop. Not responsive, not translated, no CSS variables, so changing a colour meant finding every place somebody had typed it.
- No observability. Which is also why the AWS bill kept climbing: nothing was measured, so nothing could be cut.
The useful part was that there was nobody in production yet. No users to migrate, no uptime to protect, nothing to keep alive while you work underneath it. It was a good moment to do it, and it was not going to stay that way.
What we actually changed
A workmate and I talked about it and landed in the same place: we could do better, and it had to start from the data rather than the screens. Same capabilities, new everything underneath.
Most of the rest follows from putting FHIR at the bottom. Once patients, practitioners, encounters and observations are what the standard says they are, integrating with another system is no longer a translation exercise, certification has an answer, and nothing needs a column called data for the awkward parts.
What I built
I took the frontend and the core of the backend:
- Authentication. Sessions, roles and the permission model.
- Realtime notifications. Things arriving when they happen rather than when you next reload.
- Video and phone calls. Twilio underneath, inside the product, so a consultation does not mean sending somebody a link to something else.
- Billing. Stripe, plans, subscriptions, and the state machine behind them.
- The whole interface. Responsive, translated, themed from variables, and built to look like somebody had decided what it should be.
That last one is most of why I did it. I wanted people to trust it. You click a thing, it does the thing, and nothing about it makes you wonder whether it worked. Not unexpected error 500 on a layout that has already fallen over on your phone.
Weekends
I built the first version in my own time, on weekends and evenings. I thought the product was the problem, and I would rather show that than write a document about it.
When it was ready I showed it to the CEO and the CTO. They backed it on the spot, people were moved onto it, and it stopped being a side project and became the thing we were building. It went to production, and it runs there now, with about twenty five thousand people using it.
How it was put together
- FrontendReact, TypeScript, CSS variables, i18nResponsive from the first commit, themed from one file, and translated rather than hard coded into a single language.
- BackendNestJS, PostgresA structure you could find your way around: modules per domain, one place where each thing happens, and the data model underneath it doing the work instead of a column called data.
- The data modelFHIR resourcesPatients, practitioners, encounters and observations as the standard defines them, in real tables with real relations.
- Realtime and commsWebSockets, TwilioNotifications that arrive when the thing happens, and video and phone calls that run inside the product rather than beside it.
- BillingStripePlans, subscriptions and the state machine around them, which is the part everybody underestimates.
Since then
The clean stretch did not last as long as I expected. Once it was live there were deadlines again, and debt started collecting in the new codebase the way it had in the old one. If I did it again I would spend more of the quiet part putting guard rails in, because that is the only bit you cannot add later in a hurry.
It is still the thing I am most glad I did. It went from something I was building at weekends to the product people use every day, and the crack is part of it :)