Chapter 01
The challenge
A product that began as a single-customer tool had a dozen paying organisations, all billed by hand, with their data separated only by convention.
Getting from there to a working product was not only a matter of writing code. A few things made this project harder than it first looked:
- Tenant separation relied on every query remembering a filter
- Pricing had been negotiated per customer, with no consistent plans
- The product could not be taken offline to restructure the data
- Deployments were manual and happened rarely as a result
Chapter 02
What we did
Proper multi-tenancy and self-serve subscription billing introduced underneath the live product, one tenant at a time.
1.Row-level security
Tenant isolation moved into the database so a missed filter can no longer expose another customer's data.
2.Plans and metering
Existing deals mapped onto a small set of plans in Stripe, with usage limits, trials and failed-payment recovery.
3.Zero-downtime migration
Schema changes made in backwards-compatible steps and tenants migrated individually behind a flag.
4.Automated delivery
CI/CD with preview environments, taking releases from occasional and tense to routine.
Chapter 03
The outcome
Headline result
Self-serve signup and billing replacing manual onboarding
- Tenant isolation enforced in the database, not in application code
- No downtime during the migration
Chapter 04
Built with
The tools chosen for this project, each for a reason specific to the problem rather than out of habit.
- Next.js
- NestJS
- PostgreSQL
- Stripe
- Terraform
- GitHub Actions




