Overview
A release is the set of decisions about when and how a change becomes visible to users, independent of when its code reaches the production environment. The core principle is separating deploy from release: deploying is the mechanical act of shipping code, releasing is the decision to expose a feature. Without that separation, releases are tense events. Changes pile up into a large batch waiting for the announcement date, unverified features reach users the moment their code merges, and recovery from a problem depends on another deploy. With the separation in place, deploying becomes a routine step repeated many times a day, and releasing becomes a low-risk decision that a single configuration change can advance or reverse. This page explains the principle of that separation, the environment-variable feature flag that implements it with no extra infrastructure, the development flows that deliver features spanning multiple pull requests safely, and how to roll out, roll back, and clean up flags. The pipeline that automates the deploys themselves is covered on the CI/CD page.Principles
Treat deploy and release as different events
Deploying code and releasing a feature usually happen at the same moment, but they answer different questions and belong to different owners.Deploy
Reflecting code in the production environment. A mechanical, repeatable step that can be automated and performed many times a day.
Release
Making a feature available to users. A judgment call about timing — coordination with announcements, support readiness, or business milestones.
Deploy small and often, choose release timing deliberately
Separating the two is the foundation of continuous delivery — keeping the software releasable at all times, deploying small changes frequently, and choosing release timing deliberately. Small, frequent deploys improve speed and stability together, because each change is easier to review, test, and undo. The feature flag, covered next, is what implements this separation with no extra infrastructure.Implementing the separation with feature flags
Start with a single environment-variable branch point
A feature flag is a conditional that switches behavior on or off without a code change. The simplest reliable implementation is an environment variable read at one branch point in the code. In the following example, a new label is rendered only while theFEATURE_NEW_LABEL environment variable is 'true'. Enable it locally to work on the feature, and merge with it disabled in the production environment.
FEATURE_NEW_LABEL=true to the environment — and disabling it is removing that line.
Know what the flag unlocks
The flag turns “the code is in the production environment” and “the feature is visible” into independent facts. That unlocks:Merge unfinished work
In-progress features merge into the main branch behind a flag, invisible to users, instead of aging in a long-lived branch.
Deploy often, release deliberately
Deploy to the production environment many times a day while choosing when to release each feature.
Instant rollback
If a released feature misbehaves, turn the flag off. Recovery time is minimal.
Test safely in the production environment
Enable a feature in the verification environment or for internal use first, while users in the production environment still see the old behavior.
Environment-variable flags can be implemented with no extra infrastructure, which makes them a good first step. Adopting a flag management library or service is also worth considering.
Delivering features that span multiple pull requests
Features too large for a single pull request benefit most from separating deploy and release. The development flow differs depending on whether the change can hide behind a flag, so this section covers two flows together with when to apply each.Trunk-based development
Trunk-based development is the practice of integrating all work into the main branch in small increments, avoiding branches that live for days or weeks. Long-lived branches accumulate merge conflicts, hide integration problems until the end, and produce large pull requests that are hard to review. Feature flags are what make this practical for features that take more than one pull request to build: each increment merges as soon as it passes review, guarded by the flag, and the feature is enabled only when the last piece lands. The result is that several people can build in the same area with conflicts less likely, and every merge is small enough to review properly. In the following diagram, one feature is split into three increments, each merged into the base branch with the flag off, and the release happens by enabling the flag after the last increment lands. This is the same principle as splitting work into small units — see Task Breakdown for slicing features down to a size that merges behind a flag, and Pull Requests for keeping each merge reviewable.The topic branch flow
Some changes cannot hide behind a flag — a visual overhaul or a framework migration, where the impact on the production environment cannot be hidden. The topic branch flow keeps granularity in that case. It is a three-layer branch structure that places a topic branch between the base branch and the working branches. Review and integration complete on the topic branch, and the base branch receives nothing until release, so small pull requests keep flowing without affecting the production environment. The flow works as follows.- Cut a topic branch from the base branch, then cut working develop branches from the topic branch.
- Review on PRs from a develop branch into the topic branch. This is the stage where granularity is maintained.
- When the work is ready for the production environment, merge a PR from the topic branch into the base branch. The review at this point can be minimal, because the changes were already reviewed in step 2.
- Merge the base branch’s changes into the topic branch at least once a day to keep conflicts to a minimum.
Rolling out as a sequence of controlled steps
With deploy and release separated, releasing becomes a sequence of controlled steps rather than a single event.1
Deploy with the flag off
The code reaches the production environment disabled. Users see no change; the deploy itself carries near-zero release risk.
2
Enable in the verification environment
Turn the flag on in the verification environment first. Problems surface before any user is exposed — the earlier a defect is found, the cheaper it is to fix.
3
Release in the production environment
Enable the flag in the production environment at the chosen timing. If the rollout can be scoped, start with a specific group of users and widen the audience gradually.
4
Roll back by turning the flag off
If the feature misbehaves, disable the flag.
Managing flags as temporary
Test both states
A flag doubles the states your code can be in, and both states run in the production environment at some point in the feature’s life. Write tests for each side of the branch:Remove flags that have done their job
A flag is temporary by design. Once the feature has run stably with the flag on and there is no scenario for turning it back off, remove the flag, the dead branch, and the off-state tests in one pull request. Stale flags accumulate into a combinatorial maze of states nobody tests. A trustworthy test suite is what makes this deletion safe to do promptly.Related pages
CI/CD
The automated pipeline that makes flag-off deploys repeatable many times a day.
Task Breakdown
Slice features into increments small enough to merge behind a flag.
Pull Requests
Keep each merge small and reviewable.
Test Code
Tests that protect both flag states and make flag removal safe.