Content Experimentation Best Practices
SanityOfficialPublished by Sanity
- Skill
- Guidelines
- Business and productivity
- MIT
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a structured framework for content experimentation, covering the entire lifecycle from hypothesis generation to statistical analysis. It offers best practices for designing tests, avoiding common analytical pitfalls, and integrating experimentation workflows directly into CMS architectures. The guidance emphasizes data-driven decision-making, helping teams move beyond intuition to validate content changes through rigorous testing methodologies.
What it does
- Defines frameworks for A/B and multivariate testing.
- Provides patterns for integrating experiment variants into CMS schemas.
- Outlines statistical foundations including significance and power analysis.
- Identifies common pitfalls in experiment design, execution, and interpretation.
- Offers guidance on hypothesis formulation and metric selection.
- Details architectural approaches for managing variants within content platforms.
When to use it
- When planning A/B or multivariate tests for website content.
- When designing the schema for experiment-managed content in a CMS.
- When troubleshooting inconclusive or suspicious test results.
- When establishing an experimentation culture to replace opinion-based decisions.
- When determining the required sample size for a new experiment.
- When evaluating whether to use CMS-native variants or external platforms.
Advantages
- Reduces the risk of false positives and invalid test conclusions.
- Provides clear architectural patterns for CMS-based experimentation.
- Helps teams avoid common statistical and design mistakes.
- Promotes a systematic approach to learning from content changes.
- Supports better collaboration between content and development teams.
Usage examples
- “How should I structure my CMS schema to support A/B testing on landing page headlines?”
- The skill provides specific schema examples for field-level variants or dedicated experiment documents, including fields for variant weights and status tracking.
- “Why are my A/B test results fluctuating so much?”
- The skill explains the 'peeking problem' and provides guidance on pre-calculating sample sizes and committing to run times to ensure statistical validity.
Install
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/sanity-io/content-experimentation-best-practices/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip content-experimentation-best-practices.zip -d .agents/skills
Documentation by Sanity
Content Experimentation Best Practices
Principles and patterns for running effective content experiments to improve conversion rates, engagement, and user experience.
When to Apply
Reference these guidelines when:
Setting up A/B or multivariate testing infrastructure
Designing experiments for content changes
Analyzing and interpreting test results
Building CMS integrations for experimentation
Deciding what to test and how
Core Concepts
A/B Testing
Comparing two variants (A vs B) to determine which performs better.
Multivariate Testing
Testing multiple variables simultaneously to find optimal combinations.
Statistical Significance
The confidence level that results aren't due to random chance.
Experimentation Culture
Making decisions based on data rather than opinions (HiPPO avoidance).
References
Start with the reference that matches the current problem, such as design, statistics, CMS integration, or pitfalls. See references/ for detailed guidance:
references/experiment-design.md— Hypothesis framework, metrics, sample size, and what to testreferences/statistical-foundations.md— p-values, confidence intervals, power analysis, Bayesian methodsreferences/cms-integration.md— CMS-managed variants, field-level variants, external platformsreferences/common-pitfalls.md— 17 common mistakes across statistics, design, execution, and interpretation
Files
- ATTRIBUTION.mdAdded by AutoGineer907 B
- LICENSELicence file added by AutoGineer1.0 KiB
- SKILL.md1.8 KiB
- references/cms-integration.md6.8 KiB
- references/common-pitfalls.md6.7 KiB
- references/experiment-design.md3.1 KiB
- references/statistical-foundations.md4.9 KiB
references/cms-integration.md
CMS Integration Patterns
Integrating experimentation with your CMS enables content teams to run tests without developer intervention.
Architecture Options
1. CMS-Managed Variants
Store experiment variants as content in the CMS.
Pros: Content team autonomy, version controlled Cons: More complex queries, potential publish coordination
// Experiment document
defineType({
name: 'experiment',
type: 'document',
fields: [
defineField({ name: 'name', type: 'string' }),
defineField({ name: 'status', type: 'string', options: {
list: ['draft', 'running', 'paused', 'concluded']
}}),
defineField({
name: 'variants',
type: 'array',
of: [{
type: 'object',
fields: [
defineField({ name: 'name', type: 'string' }),
defineField({ name: 'weight', type: 'number' }),
defineField({ name: 'content', type: 'reference', to: [{ type: 'page' }] }),
]
}]
}),
defineField({ name: 'startDate', type: 'datetime' }),
defineField({ name: 'endDate', type: 'datetime' }),
]
})2. Field-Level Variants
Store variants as fields on the content document.
Pros: Simpler queries, content stays together Cons: Less flexible, schema complexity
defineType({
name: 'landingPage',
fields: [
defineField({ name: 'headline', type: 'string' }),
defineField({
name: 'headlineVariantB',
type: 'string',
description: 'A/B test variant (leave empty if not testing)'
}),
defineField({ name: 'activeExperiment', type: 'string' }),
]
})3. External Experimentation Platform
Use dedicated tools (Optimizely, LaunchDarkly, VWO) with CMS content.
Pros: Robust analytics, proven platforms Cons: Additional cost, integration complexity
// CMS stores experiment IDs, platform handles assignment
defineField({
name: 'experimentId',
type: 'string',
description: 'Optimizely experiment ID'
})Implementation Pattern (CMS-Managed)
1. Experiment Schema
defineType({
name: 'experiment',
type: 'document',
fields: [
defineField({ name: 'name', type: 'string', validation: r => r.required() }),
defineField({ name: 'hypothesis', type: 'text' }),
defineField({
name: 'status',
type: 'string',
options: { list: ['draft', 'running', 'concluded'] },
initialValue: 'draft'
}),
defineField({
name: 'variants',
type: 'array',
of: [{
type: 'object',
name: 'variant',
fields: [
defineField({ name: 'id', type: 'string' }),
defineField({ name: 'name', type: 'string' }),
defineField({ name: 'weight', type: 'number', initialValue: 50 }),
]
}],
validation: r => r.min(2).error('Need at least 2 variants')
}),
defineField({ name: 'targetPage', type: 'reference', to: [{ type: 'page' }] }),
defineField({ name: 'targetField', type: 'string' }),
]
})2. Variant Content
// On the page being tested
defineField({
name: 'experimentVariants',
type: 'array',
of: [{
type: 'object',
fields: [
defineField({ name: 'experimentId', type: 'reference', to: [{ type: 'experiment' }] }),
defineField({ name: 'variantId', type: 'string' }),
defineField({ name: 'headline', type: 'string' }),
// Other variant-specific fields
]
}]
})3. Frontend Assignment
// Middleware or server-side. Returns the assigned variant id, or null when the
// experiment has no variants to assign.
function assignVariant(experimentId: string, variants: Variant[]): string | null {
if (variants.length === 0) return null
// Reuse an existing assignment, but only if it's still a valid variant.
// After variants are renamed or removed, drop the stale cookie and reassign,
// otherwise users stay bucketed to IDs that no longer exist.
const cookieKey = `exp_${experimentId}`
const existing = getCookie(cookieKey)
if (existing && variants.some(v => v.id === existing)) return existing
// Random assignment based on weights. Normalize against the total weight so
// splits work even when weights don't sum to 100 (otherwise draws above the
// sum skew to the fallback).
const totalWeight = variants.reduce((sum, v) => sum + v.weight, 0)
if (totalWeight <= 0) {
const fallback = variants[0]
setCookie(cookieKey, fallback.id, { maxAge: 30 * 24 * 60 * 60 })
return fallback.id
}
const rand = Math.random() * totalWeight
let cumulative = 0
for (const variant of variants) {
cumulative += variant.weight
if (rand < cumulative) {
setCookie(cookieKey, variant.id, { maxAge: 30 * 24 * 60 * 60 })
return variant.id
}
}
// Fall back to the last variant to absorb any floating-point remainder, and
// persist it like any other assignment so the visitor stays bucketed.
const fallback = variants[variants.length - 1]
setCookie(cookieKey, fallback.id, { maxAge: 30 * 24 * 60 * 60 })
return fallback.id
}4. Query with Variant
Resolve one experiment's assignment at a time, passing both its
experimentId and the variantId returned by assignVariant. Match on both:
variant IDs like control repeat across experiments, so filtering on
variantId alone could pick a different running experiment's row. For a page
running several experiments, resolve each one separately and merge the results.
*[_type == "page" && slug.current == $slug][0]{
...,
"experiment": experimentVariants[
experimentId->_id == $experimentId &&
experimentId->status == "running" &&
variantId == $variantId
][0]{
experimentId->{name, _id},
variantId,
headline
}
}Analytics Integration
Event Tracking
// Track experiment exposure
function trackExposure(experimentId: string, variantId: string) {
analytics.track('Experiment Viewed', {
experimentId,
variantId,
timestamp: new Date().toISOString()
})
}
// Track conversion
function trackConversion(experimentId: string, variantId: string, metric: string) {
analytics.track('Experiment Conversion', {
experimentId,
variantId,
metric,
timestamp: new Date().toISOString()
})
}Data Layer
// Push to data layer for analytics tools
window.dataLayer.push({
event: 'experiment_assignment',
experiment_id: experimentId,
variant_id: variantId
})Best Practices
Content Team Workflow
Create experiment document with hypothesis
Create variant content
Set status to "running"
Monitor results
Set status to "concluded" and record winner
Avoid Flicker
Assign variants server-side when possible
Use CSS to hide content until variant determined
Pre-render both variants, show based on assignment
Clean Up
Archive concluded experiments
Remove losing variant content
Implement winner as default
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Published by
- Sanity
- Publisher
- Sanity (Official)
- Source
- sanity-io/agent-toolkit · skills/content-experimentation-best-practices · commit 3ed3a14
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Sanity.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download content-experimentation-best-practices.zip