BLOG/DEVELOPER DAILY
THEME
DEVELOPER DAILY

Test expressions fast with the eval playground

orchstep eval evaluates any template or JavaScript expression against your real workflow context — environments, vars-files, --var and all — without running a single step. It replaces the throwaway echo-step debug loop.

Jun 19, 2026 OrchStep Team 6 minROLE: Backend DeveloperSCALE: Any

Here is the debug loop everyone knows: you are not sure what {{ vars.db_host }} resolves to under --env production, so you add a throwaway echo step, run the task, read the line, delete the step. Repeat for the next expression. It works, but it is slow and it executes real steps you may not want to run.

orchstep eval collapses that loop to one command. It answers a single question instantly: "what value would this expression have, with these exact flags?" It builds the same merged variable context orchstep run would — workflow defaults, environment groups, --vars-file, --var — then evaluates your expression against it. No steps run; nothing is touched.

It is a calculator, even with no workflow

With no orchstep.yml around, eval is a bare expression calculator — handy for checking a Sprig function or a bit of JS before you wire it in:

$ orchstep eval '1 + 2 * 3'
7
$ orchstep eval --lang template '{{ "" | default "fallback" }}'
fallback
$ orchstep eval --lang template '{{ regexFind "v([0-9]+)" "release-v42" }}'
42

Fully wrapped in {{ }} evaluates as a Go template; anything else as JavaScript — the identical rule if: and assert use at run time.

Where it shines: against a real environment

Point it at a workflow with environments and you can interrogate exactly what each --env produces. Given a service whose production environment overrides the database host and replica count:

$ orchstep eval --env production 'vars.db_host'
prod-db.internal:5432

$ orchstep eval --env production --var replicas=99 '{{ vars.replicas }}'
99

$ orchstep eval --env production 'vars.require_approval === "true" && Number(vars.replicas) > 4'
true

That third one is the real win — you can test the exact JavaScript condition a retry.when or assert will run, against the exact merged context, before committing it. Mixed text works too, with --lang template:

$ orchstep eval --env staging --lang template 'deploying {{ vars.app_name }} to {{ vars.env_name }} ({{ vars.replicas }} replicas)'
deploying checkout-service to staging (4 replicas)

Objects print as JSON; scalars print plainly:

$ orchstep eval --env staging '({host: vars.db_host, level: vars.log_level})'
{
  "host": "staging-db.internal:5432",
  "level": "debug"
}

And errors surface exactly as they would in a workflow — and exit non-zero, so eval is scriptable as a check:

$ orchstep eval 'vars.nope.deep'
failed to evaluate JavaScript expression: TypeError: Cannot read property 'deep' of undefined

--explain: not just what, but why

eval answers "what is the value." Add --explain and it answers "why is it that value — which precedence layer won." With no key it prints every effective variable and the layer that supplied it, like git config --show-origin:

$ orchstep eval --explain --env production --var replicas=20
VARIABLE  SOURCE   VALUE
region    group    eu-west-1   (over defaults)
replicas  runtime  20   (over defaults, environment)

The (over ...) column shows which lower-precedence layers each value shadowed. When a variable is mysteriously "wrong," this tells you instantly whether a group file, an environment, or a stray --var won — no more bisecting your config by hand.

The REPL

Run eval with no expression in a terminal and you get an interactive playground — dump the full merged variable map, switch languages, iterate:

$ orchstep eval -f orchstep.yml --env staging
orchstep eval - interactive expression playground
eval(auto)> vars.replicas
[javascript] 4 (truthy=true)
eval(auto)> {{ vars.db_host }}
[template] staging-db.internal:5432 (truthy=true)
eval(auto)> :vars        # dump the full merged variable map as JSON
eval(auto)> :q

What you gained

The old loopWith orchstep eval
add echo step → run → read → deleteone command, no edit
runs real steps to see a valuenothing executes
guess which layer set a variable--explain shows the winner
"will this condition pass?"test the exact JS/template expression

Why the results can't lie

eval is not a re-implementation. It calls the engine's own evaluation paths — the same condition evaluator and template pipeline a run uses. Whatever it prints is what your if:, assert, until:, or {{ }} will do at run time, byte for byte. The honest boundary: eval evaluates expressions against context; it does not execute steps, so it will not tell you whether a shell command succeeds or what a step outputs at run time. For "what does this expression resolve to" and "why," though, it is the fastest tool in the box.

Where to go next

Next time you reach for a throwaway echo step — don't. orchstep eval '<expression>' answers in milliseconds, against your real context.

#EVAL#DEBUGGING#EXPRESSIONS#DEVELOPER-EXPERIENCE
Try it in two minutes — one binary, no signup.
curl -fsSL https://orchstep.dev/install.sh | sh

RELATED — DEVELOPER DAILY