Skip to main content

Strategy Anti-Patterns

This page covers common design mistakes that make strategies brittle, confusing, or hard to trust in production.

Infinite transition loops

A classic failure mode is an unbroken cycle of always transitions:

State A --always--> State B --always--> State A

Because always transitions fire immediately on state entry, the machine can spin internally without waiting for a new event.

How to avoid it:

  • replace at least one leg of the cycle with an event-triggered transition, or
  • add a guard that genuinely breaks the loop

Dead-end states

A dead-end state has outgoing transitions on paper, but none of them can realistically fire.

Typical causes:

  • every guard on the outgoing transitions is too strict or contradictory
  • the trigger never matches the events the strategy actually receives

How to avoid it:

  • make sure at least one outgoing path is realistically reachable
  • verify stream subscriptions and trigger types match the incoming event flow

Unreachable states

An unreachable state has no path into it from the rest of the machine, unless it is the initial state.

How to avoid it:

  • inspect incoming paths on the canvas
  • use Manage Transitions grouped by target state
  • delete unused states instead of leaving dead structure in the graph

Guards that never pass

This happens when a reusable guard contains impossible or contradictory conditions, such as:

  • RSI > 200
  • two conflicting equality checks in the same required group
  • conditions built on context variables that never change

How to avoid it:

  • start with the simplest version of the rule
  • verify each condition independently
  • then combine them into the final guard

Guards that always pass

The opposite failure mode is a guard that is effectively always true.

Examples:

  • RSI > 0
  • comparing a value to itself
  • adding a broad OR branch that matches almost everything

How to avoid it:

  • tighten the threshold
  • remove trivial comparisons
  • make sure the rule describes a real market scenario rather than a near-constant state

Missing stream subscriptions

An event-triggered strategy can look structurally valid and still do nothing if it is not subscribed to the stream its transitions expect.

How to avoid it:

  • configure the correct stream in the Strategy Toolbar
  • verify the transition trigger references the same event source the strategy actually receives

Treating conditions as if they were direct transition assets

In FORJ, transitions attach guards, and guards contain conditions. Trying to reason about transitions as if each transition owns its own standalone condition set leads to confusion when you start reusing guards across multiple transitions.

How to avoid it:

  • think of conditions as building blocks
  • think of guards as the reusable transition-level asset

Editing shared guards or actions without checking reuse

Guards and actions are shared definitions. If you edit one, every transition that references it changes too.

How to avoid it:

  • review where a shared guard or action is used before editing it
  • duplicate the asset when you need similar but not identical behavior

Overloading a single state

Too many overlapping transitions from one state make runtime behavior hard to predict.

How to avoid it:

  • split broad states into smaller, more focused states
  • use routing states intentionally
  • keep each state's outgoing transitions as distinct as possible