managarten/apps/uload/docs/reports/homepage-abc-testing-plan.md
Wuesteon d36b321d9d style: auto-format codebase with Prettier
Applied formatting to 1487+ files using pnpm format:write
  - TypeScript/JavaScript files
  - Svelte components
  - Astro pages
  - JSON configs
  - Markdown docs

  13 files still need manual review (Astro JSX comments)
2025-11-27 18:33:16 +01:00

14 KiB

A/B/C Testing Implementation für uLoad Homepage

Übersicht

Dieses Dokument beschreibt verschiedene Möglichkeiten zur Implementierung von A/B/C Testing für die uLoad Homepage, von einfachen bis zu fortgeschrittenen Lösungen.

🎯 Testing-Ziele für die Homepage

Was wollen wir testen?

  1. Headlines & Value Propositions
  2. Call-to-Action Buttons (Text, Farbe, Position)
  3. Hero Section Layout (Text vs. Video vs. Interactive Demo)
  4. Social Proof (Position, Format, Inhalt)
  5. Feature Präsentation (Grid vs. List vs. Carousel)
  6. Form Fields (Mehr vs. Weniger Felder)

📊 Implementierungsmöglichkeiten

Vorteile

  • Keine externen Dependencies
  • Volle Kontrolle über Daten
  • GDPR-konform (First-Party Cookies)
  • Kostenlos
  • Server-side Rendering kompatibel

Nachteile

  • Manuelle Auswertung
  • Keine visuellen Editor
  • Mehr Entwicklungsaufwand

Implementation

1. A/B Testing Service erstellen:

// src/lib/ab-testing/service.ts
import type { Cookies } from '@sveltejs/kit';

export interface ABTestVariant {
	id: string;
	name: string;
	weight: number; // 0-100 percentage
}

export interface ABTest {
	id: string;
	name: string;
	variants: ABTestVariant[];
	active: boolean;
	startDate?: Date;
	endDate?: Date;
}

export class ABTestingService {
	private tests: Map<string, ABTest> = new Map();

	constructor() {
		// Homepage Tests konfigurieren
		this.tests.set('homepage-hero', {
			id: 'homepage-hero',
			name: 'Homepage Hero Section',
			active: true,
			variants: [
				{ id: 'control', name: 'Original', weight: 34 },
				{ id: 'value-focused', name: 'Value Proposition', weight: 33 },
				{ id: 'social-proof', name: 'Social Proof First', weight: 33 },
			],
		});

		this.tests.set('homepage-cta', {
			id: 'homepage-cta',
			name: 'Homepage CTA Button',
			active: true,
			variants: [
				{ id: 'start-free', name: 'Start Free', weight: 25 },
				{ id: 'try-now', name: 'Try Now', weight: 25 },
				{ id: 'get-started', name: 'Get Started', weight: 25 },
				{ id: 'create-link', name: 'Create Your First Link', weight: 25 },
			],
		});
	}

	getVariant(testId: string, cookies: Cookies): ABTestVariant | null {
		const test = this.tests.get(testId);
		if (!test || !test.active) return null;

		// Check for existing assignment
		const cookieName = `ab_${testId}`;
		const existingVariantId = cookies.get(cookieName);

		if (existingVariantId) {
			const variant = test.variants.find((v) => v.id === existingVariantId);
			if (variant) return variant;
		}

		// Assign new variant based on weights
		const variant = this.selectWeightedVariant(test.variants);

		// Store assignment in cookie (30 days)
		cookies.set(cookieName, variant.id, {
			path: '/',
			maxAge: 60 * 60 * 24 * 30,
			httpOnly: true,
			secure: true,
			sameSite: 'lax',
		});

		return variant;
	}

	private selectWeightedVariant(variants: ABTestVariant[]): ABTestVariant {
		const totalWeight = variants.reduce((sum, v) => sum + v.weight, 0);
		let random = Math.random() * totalWeight;

		for (const variant of variants) {
			random -= variant.weight;
			if (random <= 0) return variant;
		}

		return variants[0];
	}

	// Get all active tests for debugging
	getActiveTests(): ABTest[] {
		return Array.from(this.tests.values()).filter((t) => t.active);
	}
}

export const abTesting = new ABTestingService();

2. Server-side Load Function:

// src/routes/+page.server.ts
import { abTesting } from '$lib/ab-testing/service';

export const load: PageServerLoad = async ({ locals, cookies }) => {
	// Existing code...

	// A/B Test Variants zuweisen
	const heroVariant = abTesting.getVariant('homepage-hero', cookies);
	const ctaVariant = abTesting.getVariant('homepage-cta', cookies);

	return {
		// Existing data...
		abTests: {
			hero: heroVariant,
			cta: ctaVariant,
		},
	};
};

3. Homepage mit Varianten:

<!-- src/routes/+page.svelte -->
<script lang="ts">
	import type { PageData } from './$types';
	import HeroOriginal from '$lib/components/hero/HeroOriginal.svelte';
	import HeroValue from '$lib/components/hero/HeroValue.svelte';
	import HeroSocial from '$lib/components/hero/HeroSocial.svelte';

	let { data }: { data: PageData } = $props();

	// Hero Component basierend auf Variant
	const heroComponents = {
		control: HeroOriginal,
		'value-focused': HeroValue,
		'social-proof': HeroSocial,
	};

	const HeroComponent = heroComponents[data.abTests?.hero?.id || 'control'];

	// CTA Text basierend auf Variant
	const ctaTexts = {
		'start-free': 'Start Free - No Credit Card',
		'try-now': 'Try Now',
		'get-started': 'Get Started Free',
		'create-link': 'Create Your First Link',
	};

	const ctaText = ctaTexts[data.abTests?.cta?.id || 'start-free'];
</script>

<!-- Dynamische Hero Section -->
<HeroComponent {ctaText} />

<!-- Rest der Seite... -->

Option 2: Feature Flags mit Environment Variables

Implementation

// src/lib/feature-flags.ts
export const featureFlags = {
	newHero: import.meta.env.PUBLIC_FEATURE_NEW_HERO === 'true',
	interactiveDemo: import.meta.env.PUBLIC_FEATURE_DEMO === 'true',
	pricingCalculator: import.meta.env.PUBLIC_FEATURE_CALCULATOR === 'true',
};

// .env.local
PUBLIC_FEATURE_NEW_HERO = true;
PUBLIC_FEATURE_DEMO = false;
PUBLIC_FEATURE_CALCULATOR = true;

Option 3: URL Parameter Testing (für interne Tests)

// src/routes/+page.server.ts
export const load: PageServerLoad = async ({ url, cookies }) => {
	// Check for test parameter
	const variant = url.searchParams.get('variant');

	if (variant && ['a', 'b', 'c'].includes(variant)) {
		// Override cookie for testing
		cookies.set('ab_homepage-hero', variant, {
			path: '/',
			maxAge: 60 * 60 * 24,
		});
	}

	// Rest of load function...
};

Verwendung: https://ulo.ad/?variant=b

Option 4: Zeitbasiertes Testing

// src/lib/time-based-testing.ts
export function getTimeBasedVariant(): string {
	const hour = new Date().getHours();

	// Different variants for different times
	if (hour >= 6 && hour < 12) return 'morning';
	if (hour >= 12 && hour < 18) return 'afternoon';
	if (hour >= 18 && hour < 24) return 'evening';
	return 'night';
}

🎨 Konkrete Test-Varianten für Homepage

Test 1: Hero Headlines (A/B/C)

// src/lib/components/hero/variants.ts
export const heroHeadlines = {
	control: {
		headline: 'Short Links That Work Harder',
		subheadline: 'Professional URL management with real-time analytics',
	},
	benefit: {
		headline: 'Save 3 Hours Per Week on Link Management',
		subheadline: 'Automate your URL workflow with smart analytics',
	},
	social: {
		headline: 'Join 10,000+ Marketers Using uLoad',
		subheadline: 'The trusted URL shortener for growing brands',
	},
};

Test 2: CTA Buttons (A/B/C/D)

<!-- src/lib/components/CTAButton.svelte -->
<script lang="ts">
	interface Props {
		variant: 'start-free' | 'try-now' | 'get-started' | 'create-link';
		onClick?: () => void;
	}

	let { variant, onClick }: Props = $props();

	const configs = {
		'start-free': {
			text: 'Start Free - No Credit Card',
			color: 'bg-purple-600 hover:bg-purple-700',
			size: 'px-8 py-4 text-lg',
		},
		'try-now': {
			text: 'Try Now →',
			color: 'bg-blue-600 hover:bg-blue-700',
			size: 'px-6 py-3 text-base',
		},
		'get-started': {
			text: 'Get Started Free',
			color: 'bg-gradient-to-r from-purple-600 to-blue-600',
			size: 'px-8 py-4 text-lg',
		},
		'create-link': {
			text: '🔗 Create Your First Link',
			color: 'bg-black hover:bg-gray-800',
			size: 'px-6 py-4 text-lg',
		},
	};

	const config = configs[variant];
</script>

<button
	onclick={onClick}
	class="transform rounded-lg font-semibold text-white transition-all hover:scale-105 {config.color} {config.size}"
>
	{config.text}
</button>

Test 3: Form Variations

<!-- Variant A: Minimal -->
<form>
	<input type="url" placeholder="Paste your long URL here..." />
	<button>Shorten</button>
</form>

<!-- Variant B: With Options -->
<form>
	<input type="url" placeholder="Enter your URL" />
	<input type="text" placeholder="Custom alias (optional)" />
	<button>Create Short Link</button>
</form>

<!-- Variant C: Full Featured -->
<form>
	<input type="url" placeholder="Your URL" />
	<input type="text" placeholder="Title" />
	<textarea placeholder="Description"></textarea>
	<div class="options">
		<input type="checkbox" id="qr" />
		<label for="qr">Generate QR Code</label>
	</div>
	<button>Create Smart Link</button>
</form>

📈 Tracking & Analytics

1. Event Tracking Setup

// src/lib/analytics/ab-tracking.ts
export function trackABEvent(
	testId: string,
	variantId: string,
	action: 'view' | 'click' | 'conversion'
) {
	// Google Analytics
	if (typeof window !== 'undefined' && window.gtag) {
		window.gtag('event', 'ab_test', {
			test_id: testId,
			variant_id: variantId,
			action: action,
		});
	}

	// Custom Analytics Endpoint
	fetch('/api/analytics/ab', {
		method: 'POST',
		headers: { 'Content-Type': 'application/json' },
		body: JSON.stringify({
			testId,
			variantId,
			action,
			timestamp: new Date().toISOString(),
		}),
	});
}

2. Conversion Tracking

<script lang="ts">
	import { trackABEvent } from '$lib/analytics/ab-tracking';

	function handleFormSubmit() {
		// Track conversion
		if (data.abTests?.hero) {
			trackABEvent('homepage-hero', data.abTests.hero.id, 'conversion');
		}
		if (data.abTests?.cta) {
			trackABEvent('homepage-cta', data.abTests.cta.id, 'conversion');
		}

		// Submit form...
	}
</script>

3. Results Dashboard

// src/routes/admin/ab-tests/+page.server.ts
export async function load() {
	// Fetch test results from database
	const results = await db.query(`
    SELECT 
      test_id,
      variant_id,
      COUNT(CASE WHEN action = 'view' THEN 1 END) as views,
      COUNT(CASE WHEN action = 'click' THEN 1 END) as clicks,
      COUNT(CASE WHEN action = 'conversion' THEN 1 END) as conversions,
      COUNT(CASE WHEN action = 'conversion' THEN 1 END) * 100.0 / 
        NULLIF(COUNT(CASE WHEN action = 'view' THEN 1 END), 0) as conversion_rate
    FROM ab_events
    GROUP BY test_id, variant_id
    ORDER BY test_id, conversion_rate DESC
  `);

	return { results };
}

🚀 Implementierungsschritte

Woche 1: Basis-Setup

Tag 1: A/B Testing Service
□ Service-Klasse erstellen
□ Cookie-Management implementieren
□ Variant-Zuweisung testen

Tag 2: Homepage Integration
□ Server-side Load anpassen
□ Varianten an Frontend übergeben
□ Debug-Modus einbauen

Tag 3: Hero Varianten
□ 3 Hero-Komponenten erstellen
□ Dynamisches Rendering
□ Styling anpassen

Tag 4: Tracking
□ Analytics Events einrichten
□ Conversion Tracking
□ Test-Dashboard (simpel)

Tag 5: Testing & Launch
□ Alle Varianten testen
□ Cookie-Verhalten prüfen
□ Live schalten

Woche 2: Erweiterte Tests

□ CTA Button Varianten
□ Form Varianten
□ Social Proof Tests
□ Feature Grid Tests
□ Mobile-spezifische Tests

💡 Best Practices

1. Test-Dauer

  • Minimum: 2 Wochen
  • Optimal: 4 Wochen
  • Traffic-basiert: Min. 1000 Besucher pro Variante

2. Statistische Signifikanz

// Simple significance calculator
function isSignificant(
	controlConversions: number,
	controlVisitors: number,
	variantConversions: number,
	variantVisitors: number
): boolean {
	const controlRate = controlConversions / controlVisitors;
	const variantRate = variantConversions / variantVisitors;

	// Simple 95% confidence check
	const difference = Math.abs(controlRate - variantRate);
	const threshold =
		1.96 *
		Math.sqrt(
			(controlRate * (1 - controlRate)) / controlVisitors +
				(variantRate * (1 - variantRate)) / variantVisitors
		);

	return difference > threshold;
}

3. Test-Priorisierung

  1. High Impact: Hero, Headlines, CTAs
  2. Medium Impact: Layout, Features, Pricing
  3. Low Impact: Colors, Fonts, Micro-copy

🎯 Quick Start: Minimal Implementation

Für den schnellsten Start, hier eine minimale Implementierung:

// src/routes/+page.server.ts
export const load: PageServerLoad = async ({ cookies, locals }) => {
	// Simple 50/50 split
	let heroVariant = cookies.get('ab_hero');

	if (!heroVariant) {
		heroVariant = Math.random() > 0.5 ? 'a' : 'b';
		cookies.set('ab_hero', heroVariant, {
			path: '/',
			maxAge: 60 * 60 * 24 * 30,
		});
	}

	// Existing load code...

	return {
		// Existing data...
		heroVariant,
	};
};
<!-- src/routes/+page.svelte -->
<script>
	let { data } = $props();
</script>

{#if data.heroVariant === 'a'}
	<h1>Short Links That Work Harder</h1>
{:else}
	<h1>Save Time with Smart URL Management</h1>
{/if}

📊 Erwartete Ergebnisse

Nach 1 Woche

  • 500+ Besucher pro Variante
  • Erste Trends erkennbar
  • Qualitative Insights

Nach 2 Wochen

  • 1000+ Besucher pro Variante
  • Statistische Relevanz möglich
  • Klare Gewinner bei großen Unterschieden

Nach 4 Wochen

  • Robuste Daten
  • Signifikante Ergebnisse
  • Basis für Entscheidungen

Zusammenfassung

Empfehlung für sofortigen Start:

  1. Cookie-basierte Lösung implementieren (Option 1)
  2. Mit Hero-Headlines beginnen (größter Impact)
  3. Google Analytics für Tracking nutzen
  4. Nach 2 Wochen erste Auswertung

Diese Lösung ist:

  • Schnell implementierbar (1-2 Tage)
  • Kostenlos
  • GDPR-konform
  • Erweiterbar
  • Unabhängig von externen Services

Erstellt am: Januar 2025 Projekt: uLoad Homepage A/B/C Testing Schwierigkeit: Mittel Zeitaufwand: 2-3 Tage für Basis-Implementation