Skip to Content
Getting StartedBranching and Releases

Branching and Releases

This page explains how code moves from your feature branch to production: the promotion flow is feature branch → staging → main, with staging auto-deploying and prod requiring a manual human dispatch. It applies to every change in the monorepo — there is no direct path to main.

The reason for the two-hop flow is validation on a real environment: staging runs the same ordered deploy pipeline as prod, so a change is exercised against a live stack before anyone can promote it. And because the org’s GitHub plan lacks environment required-reviewers, the prod gate isn’t a branch protection rule — it’s the deliberate act of a human dispatching the prod workflow.

The rules

  • Open all PRs against staging, not main. A feature/fix branch never PRs straight into main (enforce-branch-flow.yml backs this up).
  • Merging to staging auto-deploys staging. staging-deploy.yml is push-triggered and runs migrate → cdk deploy → Cloudflare portal, the same ordered pipeline as prod.
  • Promote staging → main via PR once the change looks good on staging.
  • Prod deploys are manual. prod-deploy.yml is workflow_dispatch from main. Merging to main does not deploy anything.
  • CI gates PRs into both branches identically — build / typecheck / test, gates, and postgres integration (see Verify and CI).

A typical ship

git checkout staging && git pull git checkout -b evan/ops-123-my-fix # ...make changes... bun run verify # always, before pushing git push -u origin evan/ops-123-my-fix gh pr create --base staging --title "OPS-123: my fix" # after merge: staging auto-deploys; validate there, then: gh pr create --base main --head staging --title "Promote staging to main" # after merge: a human dispatches prod-deploy.yml from main

Two things to respect in this flow. Migrations run before code deploys in both pipelines — and they are forward-only: never edit a migration’s SQL after it has been applied anywhere (prod has suffered checksum-drift incidents from exactly this; CI’s migration-immutability gate now enforces it). And deploying code ahead of migrations is an outage pattern — a past prod 500 came from shipped code referencing a table whose migration hadn’t run. Trust the pipeline ordering; don’t hand-deploy around it.

Docs-only changes (README.md, CLAUDE.md, docs/**, etc.) are in CI’s paths-ignore, so a docs PR shows no CI checks — that’s expected, not broken.

Last updated on