Branching

How a project takes different paths without an if statement.

Weft has no if keyword and no switch statement. You branch by outputting null on the branches that should not run, and relying on null propagation to skip everything downstream.

It sounds indirect the first time you read it. It clicks the second time. It becomes the obvious answer the third.

The pattern

You have a router node with multiple optional outputs. At runtime, exactly one of them is non-null. Each branch's downstream nodes have required inputs. When a branch output is null, every node downstream of it skips via null propagation. Only the active branch actually runs.

The key rule

The downstream input must be required. If you mark it optional, the node will run with null as its input, which is almost never what you want. Required is the default, so usually you do not need to think about it, but this is the single most common branching bug, so it is worth repeating: required downstream inputs are what actually stop execution.

Why this is better than a switch

  • The branches are visible in the graph. You see every path simultaneously. No scrolling through an if/elif/else chain trying to figure out what the system does.
  • Each branch runs in the same graph model as the rest of your code. Nothing special, no block scoping, no control flow rules to learn.
  • Branches can produce more parallelism. The active branch can itself include parallel processing, nested groups, other routers, anything.
  • Null propagation handles every edge case you would otherwise write by hand. A subtree that skips does not leave half-processed data lying around. It just does not happen.

The approve/reject shortcut

For the most common branching case, a human picks approve or reject, HumanQuery provides a purpose-built approve_reject form field. A field with "key": "decision" creates two Boolean output ports, decision_approved and decision_rejected. At runtime exactly one is non-null (whichever button the human clicked), the other is null. Wire the Boolean outputs through a Gate (or directly to downstream Boolean-accepting ports) to drive branching.

Same null-propagation pattern, wrapped into a form field so you do not have to write the router by hand. See Put a human in the loop for the rest of the form-schema story.

Joining branches back together

You almost never need to. Each branch writes its own result to its own downstream node (a Debug, a storage write, a different side effect). Because only one branch actually produces data at runtime, the other downstream nodes skip, and there is no need to merge anything.

If you genuinely need a single downstream node to see whichever branch ran, the pattern is a Pack (or any aggregator) with one input per branch, where each input is optional so the node runs when any branch arrives. Each input port can only have one incoming edge, so you still cannot wire two branches into the same port.

What's next