Branching strategy
We use trunk-based development on every project. There is one long-lived branch — main — and it is always deployable. Feature branches are short-lived (typically less than 48 hours of life) and exist only to gather review feedback before being squash-merged back into main. We do not maintain parallel develop, release, or environment branches because they consistently produce drift, merge pain, and "works on my branch" bugs.
Branch names are namespaced: feat/<ticket-id>-short-slug, fix/<ticket-id>-short-slug, chore/<short-slug>. Commit messages follow Conventional Commits: feat:, fix:, chore:, refactor:, test:, docs:, perf:. The squash-merged commit on main becomes the single authoritative entry for the change, which keeps the history readable and changelog-friendly.
We work behind feature flags for anything user-visible that is not yet ready to ship. This lets us merge daily without releasing daily. Flags are checked into a single config file with a TTL comment so they don't quietly accumulate.