← Guides

Getting started: running your first experiment

An experiment in dif is one Markdown file in your repo. The CLI reads the active files and compiles them into a typed client your app imports at render time, so picking a variant never leaves the page. This guide goes from an empty repo to a live variant on screen.

Install the CLI

The CLI ships as a single binary, so there is no separate runtime to install. On macOS or Linux:

$ curl -fsSL https://dif.sh/install.sh | sh

Prefer a package manager? Use brew install dif-sh/tap/dif, or npm install -g @dif.sh/cli on any platform with Node 18 or newer.

Scaffold the workspace

Run dif init in your repo. The --surface flag names the first place you will run experiments. Everything lands in one dif/ folder, checked into git:

$ dif init --surface checkout
  created dif/config.yaml
  created dif/surfaces/checkout.md
  created dif/experiments/active/ and concluded/
  created dif/audiences/locale.ts, device_type.ts
  added dif guidance to CLAUDE.md, AGENTS.md

Those last guidance files teach a coding agent the workflow. That is the next guide. Skip them for now with dif init --no-agent-files if you would rather add them later.

Create the experiment

dif new writes the draft, you fill it in, then dif validate and dif build check and compile it. The owner comes from your git config user.email:

$ dif new checkout-cta-v2 --surface checkout
# fill in the hypothesis and brief, then set status: active
$ dif validate
$ dif build
  client  → dif/generated/client.ts
  context → dif/context.json

The draft starts with status: draft, today's date, and two variants (control and variant_a) split 50/50. If the surface has past results, dif new pastes the last three into the brief as a comment, so you draft from what you already learned.

Fill it in

Open the file and write the parts that need a human: the hypothesis, the brief, the rationale. Then change status: draft to status: active.

# dif/experiments/active/checkout-cta-v2.md
---
id: checkout-cta-v2
status: active
owner: ada@acme.dev
surface: checkout
hypothesis: >
  A more urgent checkout CTA will lift completed-checkout
  among returning visitors.
audience:
  include:
    - returning_visitor: true
variants:
  - id: control
    weight: 50
  - id: variant_a
    weight: 50
    summary: "Get it today"
metrics:
  primary: completed_checkout
  guardrails: [refund_rate]
created: 2026-06-17
---

## Brief

Returning visitors drop off between cart and payment. Test whether
more urgent copy closes the gap. Copy only, no layout change.

## Decision

<!-- drafted by dif conclude -->

A few rules the validator enforces: weights total 100, control stays first, and anything you name in audience (here returning_visitor) is declared in dif/config.yaml. dif validate reports every problem in one pass and points at the line.

note
dif build only emits experiments with status: active. A draft stays out of the client until you flip it, so you can commit work in progress without shipping it.

Render it

Install the SDK:

$ npm install @dif.sh/sdk

Import the generated client once at boot, then call dif() where you want the variant. The branch keys match the variant ids in your file:

// import the generated client once, at boot
import "./dif/generated/client";
import { dif } from "@dif.sh/sdk";

dif.init({ userId: () => currentUser?.id ?? null });

const cta = dif("checkout-cta-v2", {
  control: () => "Place order",
  variant_a: () => "Get it today",
});

button.textContent = cta();

dif() returns a function you call at the render site. It buckets the user, returns their variant, and fires one exposure event. There are adapters for React and Svelte if you would rather use a hook or a store.

preview
Want to see a variant before shipping? dif qa --force checkout-cta-v2=variant_a --preview-url https://staging.example.com/checkout prints a link with that variant forced. Forced views do not fire exposures.

That is the loop: write a file, build, render. When the test has run its course, dif conclude records the decision and moves the file to concluded/. To hand the same workflow to a coding agent, read on.