Skip to content

Shipping code

How a change reaches main at Changineers: branch model, PR mechanics, CI gates, dependency and secret handling.

This page describes how a change moves from an engineer’s machine to the main branch. All Changineers code is hosted in GitHub, and repositories are private by default.

We run a single long-lived main branch. Merges to main auto-deploy to the non-production beta environment. The path from beta to production is governed by Change management; merging a PR is not a production release.

Feature branches are short-lived: hours to days, not weeks. A branch should cover a feature or a slice of one that is independently shippable. Long-running work is split into smaller pieces and rolled out gradually behind feature flags rather than held on a long-lived branch.

Changes reach main through pull requests. We squash-merge. Branch protection on main requires the CI checks below to pass; human review is not required to merge.

CI on each pull request runs:

Three tools run on every PR:

  • Biome lints the codebase and checks code quality.
  • TypeScript type-checks the codebase.
  • CodeQL scans for security patterns.

Biome and TypeScript run together as pnpm lint; failures block merge. CodeQL runs report-only via GitHub Advanced Security; findings appear in the repository’s GitHub Security tab and are triaged there.

After a merge to main, smoke and browser tests run against the beta deployment. An expanded smoke and browser suite runs nightly against beta.

Reviews are encouraged but optional for routine changes. For changes that touch a high-risk component, ask for a review in Slack #dev. High-risk by convention covers security, finance, and auditing code paths.

Two pieces work together:

  • Renovate runs continuously and opens pull requests to upgrade dependencies, including security-labelled PRs when an advisory lands.
  • OSV-Scanner runs in CI on every PR and on main. It blocks merges that depend on a vulnerable version, which closes the window between an advisory landing and the corresponding Renovate PR being merged.

If OSV-Scanner blocks a merge, the fix is to apply the relevant Renovate PR (or open one manually) rather than to suppress the finding. If applying the upgrade isn’t trivial (the upgrade breaks the app, the dependency has been deprecated, no fixed version exists yet), the finding moves into Vulnerability management for triage.

Adding a dependency means trusting its maintainers, its transitive dependencies, and its build pipeline. That’s not a reason to avoid dependencies, we use plenty, but think about it before pulling in something new, especially for small problems where rolling our own would be tractable. If you’re choosing between adding a dep or writing a few hundred lines yourself, that’s a design conversation worth having; capture the call in the ADR.

Application secrets are not stored in source code. Production secrets live in AWS Secrets Manager and are fetched at runtime by the services that need them.

Trufflehog catches accidental commits of secrets:

  • Pre-commit hook on each developer’s machine catches secrets before they enter git history.
  • CI check on every PR and on main is the backstop. A merge is blocked if any secret is detected.

If a secret is committed and pushed before the local hook catches it, treat it as leaked: rotate the secret first, then remove it from history.

Changineers infrastructure (the platform, supporting AWS services, the AWS Organization itself) lives in Terraform repos and ships the same way. Non-production changes apply on merge; production changes go through the release PR in Change management.

If you find yourself wanting to make a change directly in the AWS console, stop and check whether it should be Terraform instead. The exception path (CTO-approved, time-bound elevated SSO) exists for the exceptional case; see Change management § Authorised actors.