mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-16 16:19:40 +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>
591 lines
14 KiB
Markdown
591 lines
14 KiB
Markdown
# 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
|
|
|
|
### 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:**
|
|
|
|
```typescript
|
|
// 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:**
|
|
|
|
```typescript
|
|
// 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:**
|
|
|
|
```svelte
|
|
<!-- 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
|
|
|
|
```typescript
|
|
// 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)
|
|
|
|
```typescript
|
|
// 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
|
|
|
|
```typescript
|
|
// 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)
|
|
|
|
```typescript
|
|
// 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)
|
|
|
|
```svelte
|
|
<!-- 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
|
|
|
|
```svelte
|
|
<!-- 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
|
|
|
|
```typescript
|
|
// 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
|
|
|
|
```svelte
|
|
<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
|
|
|
|
```typescript
|
|
// 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
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
□ 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
|
|
|
|
```typescript
|
|
// 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:
|
|
|
|
```typescript
|
|
// 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,
|
|
};
|
|
};
|
|
```
|
|
|
|
```svelte
|
|
<!-- 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_
|