OrchStep for early startups
Your first three engineers are shipping faster than they can document. Here's how one orchstep.yml runs the same locally and in CI, so the deploy works the same on a laptop as in the pipeline.
The first few engineers at a startup move fast and write almost nothing down. The deploy lives in one person's head and a GitHub Actions YAML that's slowly diverging from what they actually run on their laptop. Someone pushes a fix, CI does something subtly different from local, and you lose an afternoon to "works on my machine, fails in the pipeline."
At this stage you don't need a platform team or a golden path. You need one description of how the app is built and shipped — one that runs the same way whether a human types it or CI does. That's the gap OrchStep fills for an early team: the same orchstep.yml is your local command and your CI step.
What automation looks like at this stage
You have CI now (probably GitHub Actions), but it's a second, parallel definition of your build. Local has a Makefile or a deploy.sh; CI has YAML that reimplements the same steps with different env handling. Two sources of truth means two things to keep in sync, and they never stay in sync.
The fix isn't more CI — it's making CI call the same workflow you call locally. Your pipeline shrinks to "install orchstep, run the task." Everything else lives in one file every engineer can read and run.
Two workflows: CI and deploy
Split the work into a ci task (what every push must pass) and a deploy task that reuses it. The deploy gates on the target, so the same definition handles a staging push and a guarded production release.
name: app
defaults:
version: "0.2.0"
target: staging
tasks:
ci:
steps:
- name: lint
func: shell
do: echo "linting"
- name: test
func: shell
do: echo "running tests"
deploy:
steps:
- name: ci
task: ci # CI runs first, every time
- name: release
func: shell
do: echo "deploying {{ vars.version }} to {{ vars.target }}"
catch:
- name: rollback
func: shell
do: echo "rolling back {{ vars.target }}"
- name: gate
if: '{{ eq vars.target "production" }}'
then:
- name: smoke
func: shell
do: echo "running smoke tests in production"
else:
- name: note
func: shell
do: echo "staging deploy complete"# A laptop, mid-afternoon
orchstep run ci
orchstep run deploy --var version=0.3.1
# The exact same task, in CI
orchstep run deploy \
--var version=$GIT_TAG \
--var target=production
# Preview the prod path without shipping
orchstep run deploy --var target=production --dry-runThe important part is what doesn't change between those two columns. CI doesn't reimplement lint, test, and release in pipeline YAML — it installs the binary and runs the same task your engineers run by hand. The gate means production automatically gets a smoke test that staging skips, and the catch: rolls back without a hand-rolled ||. One file, and the laptop and the pipeline finally agree.
Why this scales with the team
When engineer number four joins, onboarding is "clone the repo, run orchstep menu." They see every task with single-key hotkeys, run the inner loop, and ship to staging the same day — without a Notion page that's already stale. The workflow is the documentation, and because CI runs it too, it can't rot without the pipeline going red.
orchstep menuWhat you gained
| Concern | Two definitions (local + CI) | One orchstep.yml |
|---|---|---|
| Build defined in | Makefile and pipeline YAML | one file both call |
| "Works locally, fails in CI" | constant | same task runs both places |
| Production safety | hope someone remembers | if: gate adds a smoke test |
| Rollback | || after the deploy command | catch: block |
| Onboarding | a stale wiki page | orchstep menu |
| Preview a risky deploy | push and watch | --dry-run prints the plan |
Where OrchStep is not the answer
If your whole pipeline is genuinely "run the tests, deploy on green" and your CI vendor's YAML expresses that cleanly, you may not need this yet. OrchStep earns its keep the moment local and CI start to diverge, or the moment you want a command an engineer can run on a laptop and trust in the pipeline. Don't adopt it to look like a bigger company than you are.
Where to go next
- Quick Start — your first workflow in two minutes
- Variables & Outputs — named inputs and
--var - Error Handling — retry, catch, finally, timeouts
- Previewing with Dry Run — see the prod path before you ship
The same orchstep.yml your engineers run by hand is the one CI runs. Start with orchstep init and point your pipeline at the task.
curl -fsSL https://orchstep.dev/install.sh | sh