managing-experiment-lifecycle

作成者: posthog

実験の状態遷移をガイドします:起動、一時停止、再開、終了、バリアントの出荷、アーカイブ、リセット、複製。前提条件、…をカバーします。

npx skills add https://github.com/posthog/ai-plugin --skill managing-experiment-lifecycle

Managing experiment lifecycle

This skill covers experiment state transitions — what each action does, when to use it, and how it affects variant assignment and analysis.

State diagram

draft ──launch──▶ running ──end──▶ stopped ──archive──▶ archived
                  │ │   ▲              │
                  │ pause resume  ship_variant
                  │ │   │         (also ends if running)
                  │ ▼   │
                  │ paused (flag inactive, still "running" status)
                  │
                  ├─freeze_exposure──▶ exposure_frozen ──unfreeze_exposure──▶ running
                  │                    (enrollment closed, metrics keep flowing)

Any non-draft state ──reset──▶ draft

Actions and their implications

For each action, the two key questions:

  1. Who sees what variant? (user perspective)
  2. Who is in my analysis? (statistical perspective)

Launch (experiment-launch)

Transitions draft → running. Activates the feature flag and sets start_date.

  • Preconditions: must be in draft, flag needs 2-20 multivariate variants (no specific key required; the baseline defaults to "control" when present, else the first variant)
  • Pre-launch checklist: has at least one metric? Variants correct? Flag implemented in code?
  • Variants: users start being bucketed into variants based on the configured split
  • Analysis: data collection begins from start_date

No request body needed.

Pause (experiment-pause)

Deactivates the feature flag. Users fall back to the default experience (typically control).

  • Preconditions: must be running and not already paused
  • Variants: flag is not returned by /decide — no new exposure events recorded
  • Analysis: no new data while paused, but existing data is preserved. Experiment stays "running".

No request body. Use experiment-resume to reactivate.

Resume (experiment-resume)

Reactivates the feature flag after a pause. Users are re-bucketed deterministically into the same variants.

  • Preconditions: must be paused
  • Variants: same assignment as before pause — deterministic bucketing
  • Analysis: exposure tracking resumes

No request body.

Freeze exposure (experiment-freeze-exposure)

Stops enrolling new users while everything else keeps going: already-enrolled users keep their variant, metrics keep flowing, and end_date stays null. Snapshots the already-exposed users into a static cohort and narrows every release condition on the feature flag to that cohort. Status becomes exposure_frozen.

Use for long-horizon metrics (revenue, LTV, retention, renewals) when the sample is big enough and you want to stop adding users without stopping measurement. Neither end nor pause fits that job: end stops measurement at end_date, and pause deactivates the flag for everyone.

  • Preconditions: must be running (not draft, stopped, paused, or already frozen), flag linked and not deleted, at least one release condition
  • Variants: enrolled users keep their variant (deterministic bucketing); new users no longer match the flag
  • Analysis: exposures stop growing (a flat exposure curve is expected), but metric data keeps accumulating for enrolled users

Timing: the exposure scan and cohort snapshot run synchronously inside the API call, and duration scales with the number of exposed persons — an experiment with tens of thousands of exposed users can take on the order of tens of seconds. Set expectations with the user, wait for the response, and don't treat a slow call as a failure or retry it.

Not applicable (400) for:

  • Group-aggregated experiments — the flag targets groups, not persons, and a person cohort can't freeze group-based matching
  • Experiments in a holdout — holdout assignment is evaluated before release conditions, so new users would keep entering the holdout
  • Flags with early access conditions — also evaluated before release conditions, so freezing can't stop new enrollment
  • Mostly-anonymous exposure (e.g. experiments on logged-out surfaces) — anonymous "personless" users can never match a person cohort and would silently lose their variant, so freezes with more than a small unresolved share are rejected
  • Very large exposed sets — the exposure scan is bounded by a person cap and a timeout; over either bound the API returns a clean 400 rather than freezing

When a freeze is rejected, explain which limitation applies rather than retrying — these are structural, not transient.

Interactions with other actions: ship-variant and reset strip the freeze (both also delete the snapshot cohort); end does NOT touch the flag, so ending a frozen experiment leaves the flag narrowed to the snapshot cohort. SDKs using local evaluation can't resolve static cohorts, so a frozen flag evaluates via the /decide endpoint (standard static-cohort behavior). Exposures ingested in the final moments before freezing may miss the snapshot (ingestion lag).

No request body. Use experiment-unfreeze-exposure to reopen enrollment.

Unfreeze exposure (experiment-unfreeze-exposure)

Reopens enrollment on an exposure-frozen experiment. Removes the snapshot-cohort condition and freeze markers from every release group, restoring the flag's original targeting, and deletes the snapshot cohort. Status returns to running.

  • Preconditions: exposure must be frozen (and the experiment not ended)
  • Variants: enrolled users keep their variant; new users can enroll again under the original release conditions
  • Analysis: exposures resume growing

Can introduce bias: reopening enrollment re-exposes the flag to a potentially new population. Users who enrolled before the freeze and those who enroll after the unfreeze joined at different times, and possibly under different conditions — mixing the two cohorts in one analysis can bias the results. Warn the user before unfreezing, especially after a long freeze or if the audience or product changed in between. If they only wanted to sanity-check the frozen results, they may not need to unfreeze at all.

No request body.

End (experiment-end)

Sets end_date and transitions to stopped. The feature flag is NOT modified.

  • Preconditions: must be running (launched, not already stopped)
  • Variants: users continue seeing assigned variants (flag stays active)
  • Analysis: results frozen to data up to end_date

Optional body: conclusion ("won", "lost", "inconclusive", "stopped_early", "invalid") and conclusion_comment.

Use this when you want to freeze results without changing what users see. If the experiment's exposure was frozen, ending does not strip the freeze — the flag stays narrowed to the snapshot cohort (unfreeze first, or ship a variant, if that's not desired).

Ship variant (experiment-ship-variant)

Rewrites the feature flag so the selected variant is served to 100% of users.

  • Preconditions: must be launched (running or stopped). Cannot ship from draft.
  • Variants: ALL users see the shipped variant. The flag is rewritten with a catch-all group.
  • Analysis: if still running, the experiment is also ended (end_date set)

Always confirm with the user before shipping — this permanently rewrites the feature flag.

Required: variant_key (e.g. "test"). Optional: conclusion, conclusion_comment.

Returns 409 if an approval policy requires review before the flag change.

Archive (experiment-archive)

Hides a stopped experiment from the default list view.

  • Preconditions: must be stopped (end_date set)
  • Variants: no change — flag is unaffected
  • Analysis: no change — results remain accessible

No request body. Can be restored by setting archived=false via experiment-update.

Reset (experiment-reset)

Returns an experiment to draft state. Clears start_date, end_date, conclusion, and archived.

  • Preconditions: must not already be in draft
  • Variants: flag is left unchanged — users continue seeing assigned variants
  • Analysis: previously collected data still exists but won't be included in results unless start_date is adjusted after re-launch

No request body.

Duplicate (experiment-duplicate)

Creates a copy as a new draft with fresh dates and no results.

Important: always provide a unique feature_flag_key different from the original. If the same key is used, both experiments share a flag — changes to one affect both.

Optional: custom name (defaults to "Original Name (Copy)").

Copy to project (experiment-copy-to-project)

Copies an experiment into a different project in the same organization as a new draft. Use this instead of experiment-duplicate when the copy should land in another project; use duplicate when it stays in the same project.

  • Preconditions: source must not use legacy metrics; target project must be in the same organization and you must have write access to it. Cannot copy across organizations or regions.
  • What's copied: name, description, type, parameters, filters, primary/secondary metrics (fresh uuids), stats and scheduling config, exposure criteria. Not copied: saved-metric references (project-scoped), holdout, exposure cohort, dates, results, conclusion.
  • Feature flag: target_team_id is required; feature_flag_key is optional. The resolved key is then looked up in the target project, and the lookup result — not whether you passed the key — decides what happens:
    • If feature_flag_key is omitted: it defaults to the source experiment's flag key. That key normally doesn't exist in the target project, so a new flag with it is created there. (The default can still collide — see the next point — so to be safe, pass an explicit key.)
    • If the resolved key already exists as a flag in the target project: the copy shares that existing flag instead of creating one. Both experiments then point at the same flag, so lifecycle ops (ship, pause) on either affect both. The existing flag must be multivariate with 2-20 variants, otherwise the call returns 400.
    • If the resolved key does not exist in the target project: a new, independent flag is created with that key. To guarantee independence, pass a feature_flag_key that doesn't already exist in the target.

Confirm the source experiment and target project by name before calling — this writes into a project the user isn't looking at. The returned experiment (and its id) belongs to the target project.

Decision framework

SituationActionTool
Draft ready, flag implemented, metrics setLaunchexperiment-launch
Clear winner, significant resultsShip the winning variantexperiment-ship-variant
No significant difference after sufficient timeEnd as inconclusiveexperiment-end
Something wrong, need to stop exposure temporarilyPauseexperiment-pause
Resume after pauseResumeexperiment-resume
Stop enrolling new users, keep measuring enrolledFreeze exposureexperiment-freeze-exposure
Reopen enrollment after a freezeUnfreeze exposureexperiment-unfreeze-exposure
Experiment ended, ready to clean upArchiveexperiment-archive
Need to start over with same configReset to draftexperiment-reset
Want a similar experiment with a fresh startDuplicateexperiment-duplicate
Want the same experiment in a different projectCopy to another projectexperiment-copy-to-project

Resolving experiments

All lifecycle actions require an experiment ID. If you don't have one, load the finding-experiments skill to resolve the user's reference (name, description, "latest", etc.) to a concrete ID before proceeding.

Error handling

Error messageMeaning
"Experiment has already been launched."Can't launch a non-draft experiment
"Experiment has not been launched yet."Can't end/pause/ship a draft
"Experiment has already ended."Can't end/pause a stopped experiment
"Experiment is already paused."Use resume instead
"Experiment is not paused."It's already active
"Experiment is already in draft state."Nothing to reset
"Experiment is already archived."Already done
"Experiment exposure is already frozen."Nothing to freeze
"Experiment exposure is not frozen."Nothing to unfreeze
"Cannot freeze a paused experiment. Resume it first."Resume, then freeze
"Group-aggregated experiments cannot have their exposure frozen."Structural limitation — don't retry

When you get a 400, explain the situation to the user rather than retrying.

posthogのその他のスキル

managing-experiment-lifecycle
posthog
実験の状態遷移をガイドします:起動、一時停止、再開、終了、バリアントの出荷、アーカイブ、リセット、複製。前提条件などをカバーします。
official
configuring-experiment-analytics
posthog
Configures the analytics side of a PostHog experiment — exposure criteria (default `$feature_flag_called` vs custom exposure events), primary and secondary…
official
error-tracking-hono
posthog
PostHogのHono向けエラートラッキング
official
error-tracking-react
posthog
PostHogのReact向けエラートラッキング
official
integration-android
posthog
Androidアプリケーション向けPostHogインテグレーション
official
integration-ruby
posthog
PostHog統合:Ruby SDKを使用するRubyアプリケーション向け
official
tuning-incremental-sync-config
posthog
同期の設定はExternalDataSchemaに保存され、external-data-schemas-partial-updateを使用していつでも変更できます。ほとんどの変更は非破壊的(次の同期で反映)ですが、一部(sync_typeの切り替え、プライマリキーの変更)は、同期データの破損を防ぐために慎重な対応が必要です。
official
instrument-integration
posthog
このスキルを使用して、アプリケーションにPostHog SDKを追加します。PostHogを初めてセットアップする場合や、PostHogの初期化が必要なPRをレビューする場合に使用してください。SDKのインストール、プロバイダーのセットアップ、基本設定をカバーします。任意のフレームワークや言語に対応しています。
official