Release trains: why feature flags let you skip them
A release train is a release schedule that ships on a fixed date no matter what finished in time. Code that misses the cutoff does not ship late. It waits for the next train, which might be two weeks away or six.
Rust’s stable channel runs on exactly this model. A new version cuts every six weeks, and a change rides from nightly to beta to stable on that fixed cycle, whether or not it is fully polished. Miss the window and your change waits for the next train. That makes the release predictable. It also makes the calendar the gatekeeper instead of the code.
Feature flags skip that wait. Merge finished code to main any day, gate it behind a flag, and decide when it goes live without waiting for a scheduled release to carry it. This post covers what a release train actually buys a team, what missing one costs, and how flags let you merge every day without needing a train at all.
Key Takeaways
- A release train ships on a fixed schedule. A pull request that merges after the cutoff waits for the next train, not the next deploy.
- Rust’s stable channel is a release train: a new version ships every six weeks regardless of what landed, moving from nightly to beta to stable on that cycle.
- Feature flags decouple merging code from releasing it, so you can merge to
mainevery day and flip a flag when you’re ready, with no train to catch.- The tradeoff: a train makes rollback predictable (wait for the next one) at a fixed cost. A flag makes rollback a one-line edit, but your team now owns the flag instead of the calendar owning the decision.
- Release trains still earn their place when the release itself is expensive: app store review, firmware, or anything with a hardware step a flag cannot skip.
What a release train is
The name comes from the railway: a train leaves on schedule whether every passenger made it to the platform or not. Applied to software, a release train is a fixed cadence (weekly, every six weeks, quarterly) that ships whatever has merged by the cutoff. Nobody negotiates the date per feature.
Two versions of the term show up in practice. Rust’s release process is a ship cadence: a new stable version cuts every six weeks across three channels, nightly, beta, and stable, and a change rides the train from one to the next untouched by whether it felt ready. The Scaled Agile Framework uses the term for a planning cadence instead: an Agile Release Train is a group of teams that plan, build, and ship together on a fixed program-increment schedule, independent of any single team’s velocity.
Both versions turn “when do we ship” into a scheduled event instead of a decision made feature by feature. For a PM, that means a release date you can promise before every feature is finished. For an engineer, it means a known testing window and a known cutoff, not a moving target.
The cost of missing the train
The predictability has a cost, and it lands on whoever merged late. A pull request that lands Tuesday afternoon, an hour after Monday’s cutoff, does not ship Wednesday. It waits for the next train. On a six-week cycle like Rust’s, that is six weeks of a finished change sitting on a branch, quietly drifting out of sync with everything that keeps merging to main around it.
The longer a branch waits for its train, the more it costs to land: more rebases, and more time spent re-verifying something that was correct the day it was written. The train did not reject the code. The train was not running that day.
Feature flags decouple deploy from release
Martin Fowler’s original write-up on feature toggles names the distinction a release train blurs: deploying code and releasing a feature are two different events. A release train couples them, since the thing that ships on the train is also the thing users see. A feature flag splits them back apart.
Merge the finished code behind a flag set to off, and it deploys with everything else on the next build, visible to nobody. There is no train to catch, because the code going out and the feature going live are no longer the same event.
---
id: new-checkout
status: active
owner: sam@acme.com
surface: checkout
variants:
- id: "off"
weight: 100
- id: "on"
weight: 0
---
## Brief
Ship dark behind this flag. Flip to variant "on" once design signs off,
no deploy required.
That file merges the day the code is ready, regardless of any release cutoff. Turning the feature on later is editing the weights to 0/100 and merging, which is a commit, not a wait for the next scheduled window. The frontmatter and the .md format cover the rest of what a flag file can hold. For the person who plans release dates, that moves the decision off a shared calendar and onto a flag only they need to edit.
Merge today, release whenever
A release train assumes batching work into scheduled releases is the safe way to ship. Trunk-based development assumes the opposite: merge to main continuously, and use a flag instead of a branch to keep unfinished work invisible. The two models answer the same question, “how do we avoid shipping half-finished work,” in opposite directions.
With flags in place, the team that used to wait for Thursday’s train merges on Tuesday, Wednesday, and Thursday, each change gated and dark until someone decides it is ready. The release cadence (daily, continuous, whatever CI allows) stops being tied to the feature cadence. dif validate runs in CI on every merge, so a flag with a typo in its weights fails the pull request the same way a broken test would, no train schedule required to catch it.
The tradeoff: a calendar you trust vs a flag you own
A release train is honest about one thing flags are not: it tells you exactly when the next opportunity to ship arrives, without anyone having to decide. That is real value for a team that does not want release timing to be a judgment call made in a Slack thread.
Flags trade that certainty for control, and control has upkeep. A flag that nobody flips back off does not wait politely for a train to retire it. It sits at weight: 100 indefinitely, same as any other stale config, until someone notices and removes it. Validating flags in CI catches orphaned references, but it does not replace the discipline of actually turning a flag off once the experiment is settled.
When a release train still earns its place
Not every release can be decoupled from a schedule, because the release itself needs more than a deploy to go out. An app store review cycle, a firmware push to physical devices, or a hardware rollout all have a step a flag cannot skip: something outside your repo has to approve or ship the artifact before any flag can flip. For those, the train is the actual constraint, not a convention your team chose.
For most web and backend changes, where the constraint is self-imposed rather than a store review queue, a flag removes the train rather than schedules around it. Know which kind of release you are running before you decide which model fits.
FAQ
What is a release train in software development? A release train is a fixed release schedule, like every two weeks or every six weeks, that ships whatever has merged by the cutoff. Code that merges after the cutoff waits for the next scheduled train instead of shipping on its own timeline.
Is a release train the same as a SAFe Agile Release Train? Related but not identical. A software release train is a ship cadence (Rust’s six-week stable cycle, for example). A SAFe Agile Release Train is a planning cadence: a group of teams that plan and deliver together on a fixed program-increment schedule. Both use the fixed-cutoff idea; they apply it at different layers.
How do feature flags replace a release train? By splitting deploying code from releasing a feature. Code merges and deploys on its own schedule, gated behind a flag set to off. The feature goes live later by editing the flag, a commit and a merge, with no scheduled train required.
Can you use feature flags and still keep a release train? Yes. A train still controls when a deploy goes out; a flag controls whether a given feature is visible within that deploy. Teams with a hardware or store-review constraint often keep the train for the deploy and use flags to decouple individual features from it.
Does Rust actually use a release train model? Yes. Rust’s own release process moves a change through nightly, beta, and stable channels on a fixed six-week cycle. The Rust community calls riding that cycle “the trains.”
What’s the difference between continuous deployment and a release train? Continuous deployment ships every merged change as soon as it passes CI, with no fixed schedule. A release train batches changes into a scheduled cutoff. Feature flags let a team run continuous deployment for the code while still controlling feature visibility on its own timeline.
Getting started
A release train buys a team a predictable ship date at the cost of making the calendar the gatekeeper for every feature. Feature flags buy back that control: merge to main the day the code is ready, deploy continuously, and decide when a feature goes live by editing a flag instead of waiting for the next scheduled window.
The tradeoff runs the other way too. A train retires itself on schedule; a flag only goes away when someone removes it, so the discipline of concluding a flag matters more once there is no train to force the question. For app store review cycles, firmware, or anything with a hardware approval step, keep the train. For everything else, a flag usually removes the wait rather than scheduling around it.
Install the CLI and gate your next feature instead of waiting for the next train:
npm install -g @dif.sh/cli
dif init
See feature flags in git for the full merge-to-release workflow, or the feature flags page for the rest of the model.