Implement comprehensive A/B testing with experiment tracking, variant assignment, and statistical analysis for data-driven decisions.
# A/B Testing for Google Antigravity
A/B testing enables data-driven product decisions. This guide covers A/B testing implementation optimized for Google Antigravity and Next.js applications.
## Experiment Configuration
Build a flexible experiment system:
```typescript
// lib/experiments/config.ts
export interface Experiment {
id: string;
name: string;
description: string;
variants: Variant[];
traffic: number; // Percentage of users in experiment
status: "draft" | "running" | "paused" | "completed";
startDate?: Date;
endDate?: Date;
metrics: string[];
}
export interface Variant {
id: string;
name: string;
weight: number; // Percentage within experiment
config?: Record<string, unknown>;
}
export interface Assignment {
experimentId: string;
variantId: string;
userId: string;
assignedAt: Date;
}
// lib/experiments/engine.ts
import { Redis } from "@upstash/redis";
import { createHash } from "crypto";
const redis = new Redis({
url: process.env.UPSTASH_REDIS_URL!,
token: process.env.UPSTASH_REDIS_TOKEN!,
});
export async function getExperiment(id: string): Promise<Experiment | null> {
return redis.get<Experiment>(`experiment:${id}`);
}
export async function assignVariant(
experimentId: string,
userId: string
): Promise<Variant | null> {
// Check for existing assignment
const existing = await redis.get<Assignment>(
`assignment:${experimentId}:${userId}`
);
if (existing) {
const experiment = await getExperiment(experimentId);
return experiment?.variants.find((v) => v.id === existing.variantId) || null;
}
const experiment = await getExperiment(experimentId);
if (!experiment || experiment.status !== "running") {
return null;
}
// Check if user is in experiment traffic
const trafficHash = hashUserId(userId, `${experimentId}-traffic`);
if (trafficHash >= experiment.traffic) {
return null;
}
// Assign variant based on weights
const variantHash = hashUserId(userId, experimentId);
let cumulative = 0;
let selectedVariant: Variant | null = null;
for (const variant of experiment.variants) {
cumulative += variant.weight;
if (variantHash < cumulative) {
selectedVariant = variant;
break;
}
}
if (selectedVariant) {
// Store assignment
await redis.set(`assignment:${experimentId}:${userId}`, {
experimentId,
variantId: selectedVariant.id,
userId,
assignedAt: new Date(),
});
// Track assignment event
await trackEvent("experiment_assigned", {
experimentId,
variantId: selectedVariant.id,
userId,
});
}
return selectedVariant;
}
function hashUserId(userId: string, salt: string): number {
const hash = createHash("md5")
.update(`${userId}:${salt}`)
.digest("hex");
return parseInt(hash.substring(0, 8), 16) % 100;
}
```
## React Experiment Components
Create experiment components for React:
```typescript
// components/Experiment.tsx
"use client";
import { createContext, useContext, useEffect, useState } from "react";
interface ExperimentContextType {
getVariant: (experimentId: string) => string | null;
trackConversion: (experimentId: string, metric: string, value?: number) => void;
}
const ExperimentContext = createContext<ExperimentContextType | null>(null);
export function ExperimentProvider({
children,
userId,
}: {
children: React.ReactNode;
userId: string;
}) {
const [variants, setVariants] = useState<Record<string, string>>({});
useEffect(() => {
async function loadAssignments() {
const response = await fetch(`/api/experiments/assignments?userId=${userId}`);
const data = await response.json();
setVariants(data.assignments);
}
loadAssignments();
}, [userId]);
const getVariant = (experimentId: string) => variants[experimentId] || null;
const trackConversion = async (
experimentId: string,
metric: string,
value?: number
) => {
await fetch("/api/experiments/track", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
experimentId,
variantId: variants[experimentId],
metric,
value,
userId,
}),
});
};
return (
<ExperimentContext.Provider value={{ getVariant, trackConversion }}>
{children}
</ExperimentContext.Provider>
);
}
export function useExperiment(experimentId: string) {
const context = useContext(ExperimentContext);
if (!context) throw new Error("Must be used within ExperimentProvider");
const variant = context.getVariant(experimentId);
const trackConversion = (metric: string, value?: number) => {
context.trackConversion(experimentId, metric, value);
};
return { variant, trackConversion };
}
// Variant component
export function Variant({
experimentId,
variant,
children,
}: {
experimentId: string;
variant: string;
children: React.ReactNode;
}) {
const { variant: assignedVariant } = useExperiment(experimentId);
if (assignedVariant !== variant) return null;
return <>{children}</>;
}
// Usage
function PricingPage() {
const { variant, trackConversion } = useExperiment("pricing-test");
const handlePurchase = () => {
trackConversion("purchase", 99.99);
};
return (
<div>
<Variant experimentId="pricing-test" variant="control">
<OldPricingTable onPurchase={handlePurchase} />
</Variant>
<Variant experimentId="pricing-test" variant="new-design">
<NewPricingTable onPurchase={handlePurchase} />
</Variant>
</div>
);
}
```
## Analytics Integration
Track and analyze experiment results:
```typescript
// lib/experiments/analytics.ts
import { Redis } from "@upstash/redis";
interface ConversionEvent {
experimentId: string;
variantId: string;
metric: string;
value?: number;
userId: string;
timestamp: Date;
}
export async function trackConversion(event: ConversionEvent) {
const redis = new Redis({
url: process.env.UPSTASH_REDIS_URL!,
token: process.env.UPSTASH_REDIS_TOKEN!,
});
const key = `conversions:${event.experimentId}:${event.variantId}:${event.metric}`;
await redis.hincrby(key, "count", 1);
if (event.value) {
await redis.hincrbyfloat(key, "total_value", event.value);
}
// Store for detailed analysis
await redis.lpush(`events:${event.experimentId}`, JSON.stringify(event));
}
export async function getExperimentResults(experimentId: string) {
const redis = new Redis({
url: process.env.UPSTASH_REDIS_URL!,
token: process.env.UPSTASH_REDIS_TOKEN!,
});
const experiment = await redis.get<Experiment>(`experiment:${experimentId}`);
if (!experiment) return null;
const results = await Promise.all(
experiment.variants.map(async (variant) => {
const metrics: Record<string, { count: number; total: number }> = {};
for (const metric of experiment.metrics) {
const data = await redis.hgetall(
`conversions:${experimentId}:${variant.id}:${metric}`
);
metrics[metric] = {
count: parseInt(data?.count || "0"),
total: parseFloat(data?.total_value || "0"),
};
}
return { variant: variant.id, metrics };
})
);
return { experiment, results };
}
```
## Best Practices
When implementing A/B testing in Antigravity projects, ensure consistent variant assignment across sessions, track all relevant metrics from the start, run tests long enough for statistical significance, document experiment hypotheses, clean up completed experiments, segment results by user cohorts, and use control groups for baseline comparison.This A/B Testing prompt is ideal for developers working on:
By using this prompt, you can save hours of manual coding and ensure best practices are followed from the start. It's particularly valuable for teams looking to maintain consistency across their a/b testing implementations.
Yes! All prompts on Antigravity AI Directory are free to use for both personal and commercial projects. No attribution required, though it's always appreciated.
This prompt works excellently with Claude, ChatGPT, Cursor, GitHub Copilot, and other modern AI coding assistants. For best results, use models with large context windows.
You can modify the prompt by adding specific requirements, constraints, or preferences. For A/B Testing projects, consider mentioning your framework version, coding style, and any specific libraries you're using.