Redeo Docs
DocsLADR / Best Practices

Guides

Best Practices

Best Practices

Do's and don'ts for authoring safe, efficient, and maintainable ladders. Hard-won from the reference implementation and the Crucible composition.

Authoring

Do start with the smallest ladder that meaningfully beats one-shot. A 3-step ladder that's clearly better than a single call is more valuable than a 15-step ladder that's only marginally better.

Don't add steps "because more is better." Every step costs latency and money. If removing a step doesn't measurably hurt quality, remove it.

Do name steps by their function (draft, evaluate, synthesize, refine, critique); not by their position (step1, step2). The names show up in Studio's timeline and in error messages.

Don't reuse step IDs across branches of the ladder. Every step ID must be unique within the ladder. Validation will reject duplicates.

Do set a system prompt that exactly specifies the output format you expect. Vague prompts produce vague outputs that gates can't evaluate reliably.

Don't write paragraphs of instruction when a structured output schema will do. slateWrite.match and if.jsonMatches schemas are executable specifications; the LLM either emits conforming JSON or it doesn't.

Do prefer the typed if: block over the deprecated continueIf: string. Same behavior for the simple case, but if: unlocks richer conditions when you need them.

Don't declare both continueIf: and if: on the same step. Validation rejects it. Pick one.

Safety + budgets

Do always set an executionBudget before publishing. The platform default ($5/call) is generous; consider tighter defaults for public ladders to protect callers from unexpected spend.

Don't publish a ladder without a maxSpend ceiling. An open-ended ladder is an open-ended spend commitment.

Do test your ladder at the boundary of its budget. Set maxSpend: 0.01 temporarily and verify the engine handles exhaustion gracefully (best-so-far output, best-so-far output returned (ceiling exhaustion is graceful)).

Don't rely on the platform maximums ($50, 10k hops, 5k LLM calls) as your defaults. Those exist for first-party ladders; directory ladders should be much tighter.

Do declare your real ceiling needs honestly. If your ladder needs maxHops: 30, declare it. If it works in 10, declare 10. Inflated ceilings invite callers to spend more than necessary.

Don't set maxRecursion higher than you actually use. Most ladders never need >3. Each level multiplies cost.

Do prefer hops-bounded reflexion (jumps) over recursion for retry loops. Recursion spawns child ladders; jumps just reposition the cursor. Jumps are cheaper and easier to reason about.

Don't combine dynamic: true with recursion in the same generated step config; validation rejects it (nested dynamic + recursion is a runaway risk).

Prompting inside a ladder

Do use field names as labels in your prompt. The engine renders fields as Name: <value>; design the names so they read naturally in the prompt.

Don't inject knob values as plain numbers without context. Use a system prompt that explains what the knob means; the model reads the rendered Branch Count: 5 field line, so your prompt can reference "the Branch Count field above" to give it meaning.

Do give the LLM explicit output format instructions when you intend to gate or persist via JSON. "Output JSON: {...}" beats "output JSON."

Don't ask the LLM to emit JSON in one step and parse it informally in another. Use if.jsonMatches to verify before consuming. Failed validation is a silent no-op (no error, no event). Inspect the slate after execution to verify writes occurred.

Do use few-shot examples in the system prompt when the output shape is non-obvious. One concrete example beats five lines of description.

Don't stuff entire slate contents into the prompt unconditionally. Use retrieve: for on-demand fetch, or read specific files/metatags rather than the whole folder.

Do use the nodeInfo field when running multi-node steps; telling each node "you are node 3 of 5" produces more varied drafts than anonymous parallel calls.

Don't expect multi_ingest to deduplicate for you. If your source step has duplicate outputs, dedupe upstream with a gate or downstream in the synthesizer's system prompt.

Slates + memory

Do declare metatags for any property you'll want to filter or rank on later. Metatags are the discovery layer; file contents are the data layer.

Don't use file contents to encode structured data when metatags will do. Metatags are typed, schema'd, and checkable at validation time. File contents are freeform.

Do use mergeByKey when maintaining a registry / index / knowledge graph as a JSON file. append duplicates; overwrite wipes; mergeByKey updates by key.

Don't use evictionPolicy: reject on a folder that receives schema-matched writes. The write will silently no-op every time the file is full, with no easy way to recover.

Do set per-folder token limits that reflect what's actually useful. A 5000-token lessons folder bloats every prompt that reads it.

Don't share slates across ladders via cross-ladder jumps without thinking through concurrency. The current model is single-writer-per-call; cross-ladder writes are advanced.

Do test slate persistence by calling the ladder twice with related inputs and verifying the second call sees the first call's writes.

Don't rely on slate state for correctness without a fallback. Slates can be empty (first call), corrupted (sync edge cases), or evicted (FIFO/LRU). Code defensively.

Observability + debugging

Do mark timeline-significant steps with timeline: circle. Noise steps (intermediate parsing, schema-checking) should be left unmarked or marked timeline: init.

Don't rely on log-reading for debugging. Studio and Foundry's live timelines are the primary debugging surface; every step, node, gate evaluation, and slate write is rendered visually.

Do inspect the trace when a ladder behaves unexpectedly. Look for error, node_failed, and NodeDependencyError events. Schema-matched write failures are silent no-ops — inspect the slate to verify writes occurred.

Don't ship a ladder that hits ceilings on common inputs. If your typical call hits maxHops, either raise the ceiling or restructure the ladder.

Do version-pin in production. Call with @author/name@v3 instead of @author/name so a buggy new version doesn't break your app.

Don't assume a ladder is deterministic. LLMs are stochastic; multi-node ladders more so. If you need reproducibility, set temperature to 0 in the model parameters (passed through to the provider).

Publishing + sharing

Do set config_visibility: public only if you're comfortable with the full config being disclosed. Public-free ladders are AGPL; the config is the source.

Don't publish a ladder you wouldn't be comfortable seeing called 100,000 times. Iterate privately first.

Do include a one-line description that explains what the ladder does. The library surfaces this in listings.

Don't use clickbait descriptions. Accurate descriptions help callers find the right ladder for their task.

Do pin a stable version (@v3) once you have production callers. Latest is fine for experimentation; pinned is correct for production.

Don't break old versions when shipping new ones. The platform preserves all versions; new versions are new slugs (@v4). Existing pinned callers keep working.