OrchStep for release managers
Cut, ship, verify, roll back — the release checklist as code, with retries on the flaky publish and an automatic rollback in a catch block when verification fails.
blog/release-pipelineRelease day is a sequence held together by attention. Generate the changelog, tag the version, publish to the registry (which flakes, so retry), run the post-deploy smoke, and if it's bad, roll back — fast, correctly, under pressure. Miss a step or fat-finger the rollback and a routine release becomes an incident.
The steps are always the same. The order is always the same. The only thing that changes is the version and the target. That's exactly the kind of thing you should not be running from memory at the end of a long day.
This post writes the release as an OrchStep workflow: the happy path, the retry on the part that flakes, and the rollback as structure rather than a command you hope you remember.
The release, as a workflow
cut produces the changelog and tag and hands the ref forward via an output. ship runs cut, publishes with retries, verifies, and — on failure — rolls back in a catch: and always notifies in finally:.
name: release-train
# The release manager's checklist as code: cut -> ship -> verify -> rollback.
defaults:
version: "1.4.0"
tier: staging
tasks:
# `orchstep run cut --var version=1.5.0`
cut:
steps:
- name: changelog
func: shell
do: echo "generating CHANGELOG for {{ vars.version }}"
- name: tag
func: shell
do: echo "tagging v{{ vars.version }}"
outputs:
ref: "v{{ vars.version }}"
- name: announce
func: shell
do: echo "cut {{ steps.tag.ref }}"
# `orchstep run ship --var version=1.5.0 --var tier=production`
ship:
steps:
- name: cut
task: cut
- name: publish
func: shell
do: echo "publishing {{ vars.version }} to {{ vars.tier }}"
retry:
max_attempts: 3
interval: "1s"
backoff_rate: 2.0
- name: verify
func: shell
do: echo "post-deploy smoke on {{ vars.tier }}"
catch:
- name: rollback
func: shell
do: echo "rolling {{ vars.tier }} back to the previous release"
finally:
- name: notify
func: shell
do: echo "release {{ vars.version }} -> {{ vars.tier }} finished"The three load-bearing pieces:
outputs:on the tag, referenced later viasteps.tag.ref— the ref flows through the pipeline instead of being retyped.retry:onpublish— three attempts with exponential backoff for the registry that flakes, written as four lines instead of a hand-rolled loop.catch:is the rollback — ifverifyfails, the rollback runs automatically; it's part of the release, not a command you have to remember under pressure.finally:notifies either way.
Cut, then ship
orchstep run cut --var version=1.5.0
orchstep run ship --var version=1.5.0 --var tier=productionAnd the thing every release manager wants on the morning of a big release — see the whole plan, resolved, executing nothing:
orchstep run ship --var version=1.5.0 --var tier=production --dry-runYou can confirm the version is right, the target is right, and the rollback path exists before you trigger it. That's the difference between "I'm pretty sure rollback works" and seeing it in the plan.
What you gained
| Concern | Before | With OrchStep |
|---|---|---|
| The version | retyped at each step | one --var, flows via outputs |
| Flaky publish | manual re-run | retry: { max_attempts: 3, backoff_rate: 2.0 } |
| Rollback | a command you hope to recall | catch: runs it automatically |
| Notify on outcome | easy to forget | finally: always runs |
| "Will this do the right thing?" | trust and pray | --dry-run prints the plan |
This doesn't replace your registry, your tagging, or your deploy target — they still do the work. It replaces the coordination that currently lives in your head and a wiki page. If your release is genuinely one command, keep it. When it's a sequence with a flaky middle and a rollback you rehearse, make the sequence the artifact.
Where to go next
- Error Handling —
retry,catch,finally, timeouts - Variables & Outputs — passing the tag forward with
outputs - Previewing with Dry Run — confirm the rollback path before shipping
- The Same Workflow in CI — run the release from your pipeline
Write your next release down as cut and ship, dry-run it the morning of, and stop holding the rollback in your head.
curl -fsSL https://orchstep.dev/install.sh | sh