My own data2026 onwards

Independent-traveller booking platform

A booking platform for travellers who don't fit the fixed-date group model, built solo and now the system of record for that side of the business.

Role
Sole engineer
Stack
React · TypeScript · Vite · Tailwind · Zustand · TanStack Query · Firebase Cloud Functions · Firestore

Client work. System names, screenshots and business figures are omitted.

What it took
  • One engineer, about eight months from empty repository to daily production use
  • A business already running, that could not stop while it was rebuilt
  • A team who had never used a system for this work before
The problem

Group travel has fixed dates, fixed destinations and a fixed itinerary. Independent travellers have none of that, and that side of the business had grown without a system of its own.

The work was spread across a spreadsheet and a chat thread. It functioned, but nobody could answer basic questions about it, because the answers weren't anywhere.

What I did

The full pipeline: leads through to bookings, fulfilment, payments and reporting. I built it first as a module inside an existing internal platform, then extracted it into a standalone application once it had earned the right to be one.

Identity comes from the company's single sign-on. Authorisation does not: the platform layers its own permission model on top, with role templates expanding into a granular per-feature matrix.

Reporting runs in three tiers, each gated by permission, with financial figures deliberately separated from staff performance figures.

Decisions

The parts worth arguing about.

01

The server never trusts the client's permissions

Role templates are duplicated server-side, so the function that admits a user builds their permissions from its own copy rather than from anything the browser sends. It costs a little duplication and removes a whole category of privilege escalation.

02

Money is separated from performance

Individual and staff reporting carry no financial data at all; only the financial tier does. Who gets to see revenue is a decision worth making on purpose rather than by accident of which query was convenient.

03

Extract only once it's earned

It began inside an existing platform because that was the fastest way to be useful. Pulling it out into its own application came later, when the surface was big enough to justify the cost of a separate deployment.

What came out

Live and in daily production use, and it is now the system of record for that part of the business.

I trained the team on it myself, in person. The spreadsheet they used before isn't in use any more.

A website lead integration I built went live months after launch and became a major intake channel within its first month.

What isn't there

Test coverage is thin and concentrated on the backfills and notification routing, where a mistake writes bad data at scale and fails silently. There are no frontend tests. With one engineer and that much surface, I prioritised the code where a bug does lasting damage over the code where a bug is obvious immediately. It is a real gap, not a policy.