> ## Documentation Index
> Fetch the complete documentation index at: https://docs.traffical.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Choosing an optimization algorithm

> How to pick the right algorithm and goal for an adaptive policy — and how the goal event shapes what the optimizer learns.

When you create an adaptive policy you make two decisions: **what to optimize** (the goal event and goal type) and **how to optimize it** (the algorithm). The goal matters more than the algorithm — it defines what "better" means, and it shapes how the engine models your traffic.

## Pick the goal event first

The goal event is the reward signal. Every time a user who was exposed to the policy fires the goal event, the variant they saw gets credit. Good goal events share a few properties:

* **They happen soon after exposure.** The engine re-evaluates on a fixed window (typically hourly). An event that lands days after the decision — a subscription renewal, a delivery confirmation — starves the optimizer of feedback. Prefer a leading indicator (`add_to_cart`, `signup_started`) and measure the lagging outcome with a [metric](/concepts/events-and-metrics) instead.
* **They're frequent enough to learn from.** A variant needs a steady stream of rewards to separate from its peers. If the event fires a handful of times a day, an adaptive policy will technically work but converge very slowly — a static [A/B test](/experimentation/ab-testing) may serve you better.
* **They're attributed to the same unit.** The event's `unitKey` must match the unit that received the exposure (usually the user). Events fired before a user's first exposure don't count toward any variant.
* **They fire once per action.** Double-firing (a `useEffect` re-running, a retry loop) silently inflates one variant's rewards. Guard the track call so one real-world action produces one event.

## The goal type: how events become a score

The goal type tells the engine how to aggregate a user's goal events into a per-user score:

| Goal type           | Per-user score                                       | Choose it when                                                                                                                                      |
| ------------------- | ---------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Conversion rate** | Did this user fire the event at least once? (yes/no) | The decision is binary — signed up or didn't, purchased or didn't. Extra events per user carry no extra meaning.                                    |
| **Count**           | How many times did this user fire the event?         | Frequency is the point — product views, article reads, plays per session. Ten views really is better than one.                                      |
| **Sum**             | The total value of this user's events                | Events carry different values — revenue, watch time, items per order. Define a `valueType` on the event so each `track` call contributes its value. |

The goal type changes the statistics under the hood, not just the arithmetic:

* **Conversion rate** goals use a Bayesian success/failure model — the textbook approach for binary outcomes.
* **Count and sum** goals make the engine measure both the average per user *and* the natural spread of per-user values in your actual traffic. No distribution is assumed: if your users' values vary wildly (a few whales among many small orders), the engine sees that spread and shifts traffic more cautiously; if they're consistent, it converges faster. This is why choosing the right goal type matters — it's not a label, it's the model.

When you pick a goal event in the dashboard, Traffical suggests a goal type from the event's value type — currency-valued events suggest **sum**, everything else **count** — and you can always override it. You can also change the goal type later in the policy's settings: the attached primary metric follows the goal automatically, so measurement always judges the same quantity the optimizer optimizes.

## No events? Pick a goal metric

The goal event is a convenience: what the optimizer actually reads is the policy's **primary metric**, which the goal event resolves to at creation (the dialog shows it — "Optimizing metric: *purchase Count*"). On warehouse-only projects with no event definitions, the goal step is a metric picker instead: choose any fact-backed conversion, count, or sum metric and the goal type follows the metric. See [Optimizing with your warehouse](/experimentation/optimization#optimizing-with-your-warehouse) for the full recipe.

A goal metric whose desired direction is *decrease* (an error rate, a latency, a cancellation count) makes the optimizer **minimize** — traffic shifts toward the variant with the lowest per-user score. Ratio metrics can't be goals: their reward would use only the numerator.

The goal can also be a [composite metric](/dashboard/metrics#composite-metrics) — a weighted sum like *revenue minus cost* — so the optimizer trades several outcomes off against each other directly instead of chasing one event.

<Tip>
  If your reward is revenue-like, a handful of extreme users can dominate the signal. Consider winsorizing the corresponding metric so a single outlier order doesn't swing the verdict — see [Metrics](/dashboard/metrics).
</Tip>

## Pick the algorithm

Start with Thompson Sampling. Deviate only when you have a specific reason:

| Algorithm                       | How it explores                                                                                                                                  | Reach for it when                                                                                                                                                  |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Thompson Sampling** (default) | Samples from its current uncertainty — explores a lot when unsure, exploits when confident                                                       | Almost always. Works with all goal types and needs no tuning.                                                                                                      |
| **Epsilon Greedy**              | Sends a fixed share of traffic (ε) to random variants, the rest to the current best                                                              | You need a predictable, budgeted exploration rate you can explain in one sentence.                                                                                 |
| **UCB1**                        | Deterministic optimism — tries whichever variant has the highest plausible upside, with bounds that scale to the measured spread of your rewards | You want a parameter-free, non-random explorer. Works with all goal types; Thompson Sampling remains the recommended default.                                      |
| **Contextual Bandit**           | Learns a per-user model from context features and personalizes the choice                                                                        | The best variant genuinely differs per user (device, engagement level, referral source) and you can describe users with a few features available at decision time. |

Two quick sanity checks before choosing anything other than Thompson Sampling:

1. **Personalization or optimization?** If you expect one variant to be best for everyone, you don't need a contextual bandit — the simpler algorithms converge faster on a single winner.
2. **Do you actually need determinism?** UCB1's appeal is being non-random, but Thompson Sampling's randomness is exactly what makes it robust to noisy early data.

## Creating the policy in the dashboard

In **Layers**, create a policy and set the **Policy Type** to **Adaptive** ("AI-optimized allocation. The algorithm adjusts splits based on reward signals."). The adaptive configuration section then asks for:

* **Algorithm** — the four options above, Thompson Sampling preselected.
* **Goal Event** — a dropdown of your defined events. This is the reward signal; everything in [Pick the goal event first](#pick-the-goal-event-first) applies here.
* **Goal Type** — how the goal event aggregates per user (see [The goal type](#the-goal-type-how-events-become-a-score)). Preselected from the event's value type; override it if the suggestion doesn't match your intent.
* **Min Exposures** (default 100) — the engine won't move traffic until the policy has this many exposures. Raise it for noisy goals; lowering it below \~100 mostly buys you noise.
* **Window Size** (default 1 hour) — how often the engine re-evaluates. Shorter windows react faster but on less data per step. Hourly is right for most traffic levels.

For a **Contextual Bandit** the goal step is the same as above (goal event, or a goal metric on projects without events); you'll also list the **Context Fields** the SDK should send for personalization (these must be allow-listed for logging — see [Optimization](/experimentation/optimization#context-logging-allowlist)). Fields already used by other contextual policies in the project appear as one-click suggestions.

For per-entity learning (each product or merchant gets its own bandit) and dynamic allocations, see [Per-entity bandits](/experimentation/optimization#per-entity-bandits).

## What to expect while it runs

The optimizer is deliberately careful with your traffic:

* **It ramps, it doesn't jump.** Even when one variant looks like a clear winner, traffic moves by a capped step per window (20% by default) and every variant keeps a small exploration floor. A decisive winner typically goes from an even split to \~95% over a few windows, not instantly.
* **Phases tell you where it is.** The policy page shows *exploring* → *learning* → *converging* → *converged* as the leader's win probability grows.
* **Converged is not the same as proven.** "Converged" means the optimizer is confident enough to concentrate traffic. If you attached a primary metric, the measured verdict (the anytime-valid confidence interval) is the standard of evidence for promoting the winner — and it's deliberately stricter. A policy can be converged while the measured lift is still inconclusive; the dashboard will tell you to keep it running before completing.
* **Bandits trade measurement speed for regret.** As traffic concentrates on the leader, the other variants accrue data more slowly, so statistical significance arrives later than it would in a fixed 50/50 test. If you need a clean, fast, stakeholder-ready verdict, run an [A/B test](/experimentation/ab-testing); if you want to stop paying the cost of the losing variant as soon as possible, use an adaptive policy.

## Next steps

<CardGroup cols={2}>
  <Card title="Optimization" icon="brain" href="/experimentation/optimization">
    Setup walkthrough, tuning knobs, per-entity and contextual bandits.
  </Card>

  <Card title="Statistics" icon="chart-line" href="/statistics/overview">
    How measured significance works — and why it gates promotion.
  </Card>
</CardGroup>
