mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-15 06:01:09 +02:00
Move inactive projects out of active workspace: - bauntown (community website) - maerchenzauber (AI story generation) - memoro (voice memo app) - news (news aggregation) - nutriphi (nutrition tracking) - reader (reading app) - uload (URL shortener) - wisekeep (AI wisdom extraction) Update CLAUDE.md documentation: - Add presi to active projects - Document archived projects section - Update workspace configuration Archived apps can be re-activated by moving back to apps/ 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
14 KiB
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?
- Headlines & Value Propositions
- Call-to-Action Buttons (Text, Farbe, Position)
- Hero Section Layout (Text vs. Video vs. Interactive Demo)
- Social Proof (Position, Format, Inhalt)
- Feature Präsentation (Grid vs. List vs. Carousel)
- Form Fields (Mehr vs. Weniger Felder)
📊 Implementierungsmöglichkeiten
Option 1: Einfache Cookie-basierte Lösung (Empfohlen für Start)
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
- High Impact: Hero, Headlines, CTAs
- Medium Impact: Layout, Features, Pricing
- 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:
- Cookie-basierte Lösung implementieren (Option 1)
- Mit Hero-Headlines beginnen (größter Impact)
- Google Analytics für Tracking nutzen
- 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