Development Workflow

How work gets done at Olly: infrastructure, editor loop, git, tests.

Infrastructure

Four boxes in a single Hetzner VPC. One dev box, three runners.

flowchart LR
  subgraph VPC["Hetzner VPC · 10.0.1.0/24"]
    dev1["dev-1
Tilt + docker-compose
14 services + infra"] dev2["dev-2
gh-runner"] ci["ci
gh-runner"] uat["uat
gh-runner"] end eng["Engineers"] -->|ssh/mosh| dev1 gh["GitHub Actions"] --> dev2 gh --> ci gh --> uat
BoxRole
dev-1Primary dev environment. Tilt, docker-compose, nginx for *.dev.hiolly.com
dev-2 · ci · uatGitHub Actions runners. UAT-as-environment doesn't exist yet — conscious choice while the stack is still moving fast.

The inner loop

VS Code over SSH → Claude Code in-terminal → Tilt auto-rebuilds on save. Diverging work lives in a git worktree so Tilt keeps running on the main checkout.

flowchart LR
  vsc["VS Code · Remote-SSH"] --> cc["Claude Code
+ skills"] cc --> wt["git worktree
(if diverging)"] wt --> tilt["Tilt on dev-1
hot reload"] tilt --> svc["Go services"] tilt --> infra["Postgres · Redpanda ·
Keycloak · Temporal · OTel"] svc --> web["*.dev.hiolly.com"]

Claude skills

Git workflow

PR-based in theory; sliding scale by blast radius in practice.

flowchart LR
  c["Change"] --> q{"Blast radius?"}
  q -->|DB / cross-service| pr["PR → review → CI"]
  q -->|one service| pr2["PR, often self-merged"]
  q -->|docs / config| dir["Direct push to main"]
  pr --> main["main"]
  pr2 --> main
  dir --> main
  main --> tilt["dev-1 Tilt rebuilds"]
Honest note: the PR-for-everything ideal isn't enforced. Small team, one environment — fix is to make review cheap (fast CI, reviewer skills, smaller PRs) rather than add ceremony.

Tilt

The control plane for dev-1. One UI for services, infra, logs, and links.

WhatWhy
Hot-reload Go services on saveSub-second feedback, no docker build loop
Unified log stream (14 services)One place to grep
Infra as first-classSame up/down UX for app and Kafka, Postgres, Keycloak, Temporal, OTel, APISIX
Health gatesPostgres up → migrations → services → seeders, in order
Port-forwards + linksClick to any dashboard (Kafka UI, Temporal, Mailpit, OpenSearch)

Tests

Four layers. We lean on the outer layers more than typical — the Tilt + OTel inner loop catches most drift live.

flowchart TB
  unit["Unit · Go per package
pure logic, no I/O"] svc["Service · Go per service
real Postgres + Kafka via testcontainers"] e2e["E2E · tests/e2e
cross-service, 2PC + trace correlation"] pw["Playwright · tests/playwright
employer (46 specs) · member (WIP)"] unit --> svc --> e2e --> pw