# Effective Arc

> Idioms for safe and predictable Arc automations, with the reason for each.

These idioms make Arc automations safe and predictable. Each one gives the reason in one or two sentences.

## Sequences and stages

### Put safety checks first

When more than one transition in a stage is true in the same cycle, the first one in the code wins. Put the abort checks at the top of each stage, and the normal transitions after them.

```arc
stage pressurize {
    1 -> press_vlv_cmd
    tank_pt > 450 => abort
    abort_cmd == 1 => abort
    tank_pt > 300 => hold
}
```

See [conditions and transitions](https://docs.synnaxlabs.com/reference/control/arc/execution-model#conditions-and-transitions).

### Watch for unsafe values in a stage, not in a gate

A write or a wait needs no stage. But while a sequence waits at a gate (`tank_pt > 300 => next`) or a `time.wait`, nothing else in that sequence runs. A stage runs all its flows at the same time, so its safety checks stay active while it waits. See [steps of a sequence](https://docs.synnaxlabs.com/reference/control/arc/execution-model#steps-of-a-sequence).

### Keep a test and its abort in one sequence

Make each test one sequence of named stages, and reach the abort stage by name from each stage that needs it. This keeps the test and its safety actions together. A separate top-level abort also works: a transition to it stops the whole sequence that holds the transition. Never put the abort stage right after a write, a wait, or a gate step, because those steps move on to the next step in the code. See [write a test sequence](https://docs.synnaxlabs.com/reference/control/arc/test-sequences).

### Add a timeout to each stage that waits

A stage that waits for a value that never comes holds the test forever. Add `time.wait(duration=60s) => abort` as the last transition of the stage. See [add a timeout](https://docs.synnaxlabs.com/reference/control/arc/test-sequences#add-a-timeout).

### Show the stage to operators

The Console does not show which stage runs. Write a number for each stage to a channel, and show it with a state indicator on the schematic. See [start a sequence from a schematic](https://docs.synnaxlabs.com/reference/control/arc/start-from-schematic#show-the-stage-on-the-schematic).

## Conditions and values

### Compare values explicitly

Write `start_cmd == 1`, not `start_cmd`. A number used as a condition works, but the compiler warns: `numeric conditions are deprecated; use an explicit comparison like x != 0`.

### Use `=>` for conditions and `->` for values

`->` passes every value, true or false. `=>` passes only new true values. `tank_pt > 300 -> next` compiles with no warning, but moves on at the first `tank_pt` value, even when it is false. Use `=>` for every transition. See [`->` and `=>`](https://docs.synnaxlabs.com/reference/control/arc/execution-model#conditions-and-transitions).

### Return the type of the channel

A function that writes to a `uint8` command channel must return `u8`. A `bool` result does not compile: `func bang_bang output type bool does not match channel heater_cmd value type u8`. Use `u8(on)` to turn a `bool` into `0` or `1`.

### Plot a value before you set a limit

Write a computed value, such as a rate of change, to a channel and plot it during a real run before you choose an abort limit. A derivative of a noisy sensor is very noisy, so smooth the sensor first. See [compute values from channels](https://docs.synnaxlabs.com/reference/control/arc/compute-values#smooth-a-noisy-sensor).

## Control

### Give automations a lower authority than operators

An automation writes at 255 if you do not set an authority, so an operator cannot take control from it. Add `authority 100`, and raise it to 255 only in the abort stage. See [control authority](https://docs.synnaxlabs.com/reference/control/authority?client=arc#set-authority). Then a start button on a schematic with a higher authority takes the valves from the automation. See [troubleshoot control](https://docs.synnaxlabs.com/reference/control/troubleshooting#the-stage-indicator-shows-a-running-sequence-but-the-valves-do-not-move).

### Always give the authority line a default

`authority (vent_vlv_cmd 100)` without a default leaves the other channels, and the index channels, at 255. An operator who takes `vent_vlv_cmd` then loses every write to it. Write `authority (100 vent_vlv_cmd 200)` instead. See [control authority](https://docs.synnaxlabs.com/reference/control/authority?client=arc#set-authority).

### Stop a test before you redeploy

A redeploy starts the automation again from the start, and every `$=` value is lost. A sequence that starts at deploy, such as `true => press`, goes back to its first stage, which can close valves with the tank under pressure. A sequence that a button starts stops and waits for the next click.

## Functions

### Keep side effects out of functions with no upstream trigger

A function call with no upstream trigger that reads a channel runs in every cycle while its stage is active. A call to `ranges.end` or `status.set` inside such a function repeats in every cycle. Call those functions directly in the stage, with literal or variable arguments: `ranges.end(key=range_key)`. See [mark each run with a range](https://docs.synnaxlabs.com/reference/control/arc/test-sequences#mark-each-run-with-a-range).

### Keep state in the right place

A `$=` variable in a function keeps its value between calls, but goes back to its first value each time its stage starts. To keep a value across stages, declare it with `$=` in the sequence. See [entering a stage](https://docs.synnaxlabs.com/reference/control/arc/execution-model#entering-a-stage).
