Our goal is to establish a predictable flow for feature development and release management.
Work on a feature should branch off either a stable release branch or a release candidate (RC) branch. This ensures that development starts from a trusted, validated state of the code. If we are able to have a continuous integration pipeline, which include unit tests, integration tests, and functional tests, then we can branch off from the main branch.
Changes are developed in the feature branch, then submitted via a PR. Once approved and merged, the work flows back into the release candidate stream.
Every merge into the main branch is automatically deployed to all active development environments. Each environment is configured with its own environment variables, ensuring that testing and integration reflect the intended setup.
- Dev remains the integration environment for the main line of development.
- Feature branches may also be deployed into dedicated dev slots for targeted testing.
- Multiple active development versions can coexist, each with isolated environment variables.
After merging, a release candidate can be created by tagging the commit. At this point, testing and validation can proceed against a candidate that reflects the intention of a stable point of the codebase. Example: v3.3.0-rc.1.
We use manual deployments from branches, with safeguards to enforce stability:
- Production: Only release branches may be pushed to production.
- Staging: Only stable candidates may be deployed to staging.
When an RC is accepted, promote it by creating a release branch from the RC commit: release/<major>.<minor> (e.g., release/3.3). The final production deployment is cut on that release branch.
If fixes are needed after a release (which is common), create a hotfix branch from the release branch (not from a tag). Changes are then submitted as PRs into the release branch, and propagated into all active branches to ensure consistency across supported lines.
We will favor creating a new branch of the main for any release including hotfixes. Every hotfix applied to a release must also land in main to prevent divergence:
- Open PR #1 from
hotfix/v<major>.<minor>.<patch>intorelease/<major>.<minor>(e.g.,hotfix/v3.3.1→release/3.3) and ship the patched release from the release branch. - Open PR #2 with the same change (or a clean cherry-pick) from the hotfix branch into
main. If conflicts arise, resolve them during PR #2 so thatmainremains a superset of what’s in the release line.
Use consistent, machine- and human-friendly naming. Examples:
- Release candidate:
v<major>.<minor>.<patch>-rc.<n>(e.g.,v3.3.0-rc.1) - Stable candidate:
v<major>.<minor>.<patch>-rc.<n>-stable(e.g.,v3.3.0-rc.1-stable)
- Trunk during transition:
release/v3.3.0(temporarily used asmain) - Release maintenance (created at release promotion):
release/<major>.<minor>(e.g.,release/3.3) - Feature:
feature/<issue-id>-<short-name>(e.g.,feature/RS-1234-lead-attribution) - Hotfix:
hotfix/v<major>.<minor>.<patch>(e.g.,hotfix/v3.3.1)
Release candidate (RC) tags flag a commit as a stable point that could be released. An RC tag is not an obligation to release; it is a checkpoint used for validation, staging, and potential promotion. Multiple RCs may be created as stability improves. Stable candidate tags (suffixed with -stable) explicitly mark RCs that are cleared for staging deployment. Only release branches are considered production-ready, and deployments to production are always cut from the corresponding release branch.
- Treat the
3.3branch as the trunk (as we wouldmain). - When the transition is complete, we will rename branches appropriately.
- Immediate next step: cut an RC tag for 3.3 as soon as possible (for example,
v3.3.0-rc.1), and upon acceptance, createrelease/3.3from that RC commit before finalizing production deployments from that branch.
The following concepts are identified as valuable for future inclusion in this guide:
- Environment Management: How environment variables are versioned, documented, and synchronized across dev, staging, and production.
- Rollback Strategy: Guidance on reverting to a previous release branch or candidate if a deployment fails.
- Testing and Quality Gates: Defining which levels of tests (unit, integration, performance) must pass before promoting RCs to stable.
- Feature Flag Lifecycle: Standards for naming, usage, and cleanup of feature flags to ensure maintainability.