How we build

Small team, shared foundations

Every Harbor Media product is built the same way, from the same parts, and checked the same way before it ships.

The approach

Six habits we build by

None of these are clever. Doing them every time is the point.

  1. Start from building blocks

    Harbor Forge keeps the parts every product needs (sign-in, the server, the database, logging, deploys, CI) as versioned blocks. A new product is composed from them, and records which versions it uses.

  2. Share one sign-in

    Our identity server handles login for the apps built on it, with two-factor and passkeys. The apps never see a password; sign-in tokens stay on the server.

  3. Use one design system

    Colour, type and spacing come from one set of tokens, in light and dark. A contrast check runs in CI, so an unreadable colour fails the build.

  4. Test what people actually use

    Unit tests run on every change. After a release, smoke checks hit the live site and its API, and a failed check rolls the release back.

  5. Ship to the web, iPhone and Android

    Native Swift and Kotlin apps for each phone and a fast web front end, all talking to one server. iPhone builds go to TestFlight for testing before anything else.

  6. Check it where people see it

    A healthy server is not the same as a working page. We open it in a browser, at phone width as well as desktop, and install the build on a phone.

Harbor Forge

The block catalogue, as it prints

This is real output from our command line. Every block is marked beta: a block only earns stable by surviving real use.

More about Harbor Forge

$ hm-forge catalogueauth  beta  auth-bff            1.0.0  auth.session auth.oidcbackend  beta  node-app            1.1.0  http.server secrets.envdatabase  beta  db-postgres         1.0.0  db.postgres db.migrationsdeploy  beta  deploy-droplet      1.6.1  deploy.release deploy.rollbackobservability  beta  obs-health-version  1.0.0  obs.health obs.version  beta  obs-logging         1.0.0  obs.loggingci  beta  ci-node             1.0.0  ci.node ci.deploy ci.secretscan$ 

The stack

What it's made of

  • Phones

    Swift and SwiftUI on iPhone. Kotlin on Android.

  • Web

    Astro for sites like this one, React and Next.js for web apps.

  • Servers

    Node.js and PostgreSQL, behind nginx, on DigitalOcean.

  • Identity

    Keycloak, with OpenID Connect and passkeys.

  • Releases

    GitHub Actions: tests, build, deploy, smoke checks and rollback.

  • Design

    One token file for colour and type, generated for every surface.

Rules we keep

Things we don't do

  • Write our own login code

    Sign-in comes from the shared identity server, never from code written fresh for one app.

  • Build ahead of a need

    A block gets built when a product actually needs it, not to round out a catalogue.

  • Write legal documents

    Lawyers write terms and privacy policies. We write software.

  • Overstate where things are

    Every product on this site carries its real status, including the paused ones.

Get in touch

Curious about how something works?

Ask. We're happy to talk about how we build.

Or email [email protected]