Product Analytics
Event Taxonomy Design
Event Naming Convention
<object>_<action>
Ornekler:
user_signed_up
page_viewed
button_clicked
feature_activated
subscription_started
payment_completed
item_added_to_cart
search_performed
Event Schema (TypeScript)
interface AnalyticsEvent {
event_name: string;
timestamp: string; // ISO 8601
user_id: string;
anonymous_id?: string; // pre-auth tracking
session_id: string;
properties: Record<string, unknown>;
context: EventContext;
}
interface EventContext {
app_version: string;
platform: "web" | "ios" | "android";
locale: string;
timezone: string;
page_url?: string;
referrer?: string;
utm?: UTMParams;
device?: DeviceInfo;
}
interface UTMParams {
source?: string;
medium?: string;
campaign?: string;
term?: string;
content?: string;
}
Event Categories
| Kategori | Ornek Eventler | Amac | |----------|---------------|------| | Identity | user_signed_up, user_logged_in | Kim? | | Navigation | page_viewed, tab_switched | Nerede? | | Interaction | button_clicked, form_submitted | Ne yapti? | | Transaction | purchase_completed, subscription_started | Para akisi | | Feature | feature_activated, feature_used | Deger bulma | | System | error_occurred, api_timeout | Saglik |
Tracking Plan Template
const trackingPlan = {
"user_signed_up": {
description: "Kullanici kayit tamamladi",
properties: {
method: { type: "string", enum: ["email", "google", "github"], required: true },
referral_code: { type: "string", required: false },
plan: { type: "string", enum: ["free", "pro", "enterprise"], required: true },
},
triggers: ["Registration form submit"],
owner: "growth-team",
},
"feature_activated": {
description: "Kullanici bir feature'u ilk kez kulandi",
properties: {
feature_name: { type: "string", required: true },
activation_method: { type: "string", required: true },
time_since_signup_hours: { type: "number", required: true },
},
triggers: ["First use of any tracked feature"],
owner: "product-team",
},
};
AARRR Funnel (Pirate Metrics)
Funnel Tanimlari
Acquisition --> Activation --> Retention --> Revenue --> Referral
(Edinme) (Aktiflesme) (Tutunma) (Gelir) (Yonlendirme)
| Stage | Tanim | Ornek Metrik | Hedef | |-------|-------|-------------|-------| | Acquisition | Kullanici siteye geldi | Unique visitors, signup rate | %3-5 signup | | Activation | "Aha moment" yasandi | Onboarding completion, first value | %40-60 activation | | Retention | Geri geldi | D1/D7/D30 retention | D7 > %20 | | Revenue | Para odedi | Conversion to paid, ARPU | %2-5 conversion | | Referral | Baskasini getirdi | Invite sent, viral coefficient | K > 0.5 |
Funnel Analysis Query
-- Acquisition -> Activation -> Retention funnel
WITH funnel AS (
SELECT
user_id,
MIN(CASE WHEN event = 'user_signed_up' THEN timestamp END) AS signed_up_at,
MIN(CASE WHEN event = 'onboarding_completed' THEN timestamp END) AS activated_at,
MIN(CASE WHEN event = 'feature_used' AND day_number >= 7 THEN timestamp END) AS retained_at,
MIN(CASE WHEN event = 'subscription_started' THEN timestamp END) AS converted_at
FROM events
WHERE timestamp >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY user_id
)
SELECT
COUNT(signed_up_at) AS acquisitions,
COUNT(activated_at) AS activations,
ROUND(100.0 * COUNT(activated_at) / NULLIF(COUNT(signed_up_at), 0), 1) AS activation_rate,
COUNT(retained_at) AS retained,
ROUND(100.0 * COUNT(retained_at) / NULLIF(COUNT(activated_at), 0), 1) AS retention_rate,
COUNT(converted_at) AS converted,
ROUND(100.0 * COUNT(converted_at) / NULLIF(COUNT(retained_at), 0), 1) AS conversion_rate
FROM funnel;
Cohort Analysis
Retention Cohort Query
-- Weekly retention cohort
WITH user_cohort AS (
SELECT
user_id,
DATE_TRUNC('week', MIN(timestamp)) AS cohort_week
FROM events
WHERE event = 'user_signed_up'
GROUP BY user_id
),
user_activity AS (
SELECT
e.user_id,
uc.cohort_week,
DATE_TRUNC('week', e.timestamp) AS activity_week,
(DATE_TRUNC('week', e.timestamp) - uc.cohort_week) / 7 AS week_number
FROM events e
JOIN user_cohort uc ON e.user_id = uc.user_id
)
SELECT
cohort_week,
week_number,
COUNT(DISTINCT user_id) AS active_users,
ROUND(100.0 * COUNT(DISTINCT user_id) /
FIRST_VALUE(COUNT(DISTINCT user_id)) OVER (
PARTITION BY cohort_week ORDER BY week_number
), 1) AS retention_pct
FROM user_activity
GROUP BY cohort_week, week_number
ORDER BY cohort_week, week_number;
Cohort Visualization Data
interface CohortData {
cohort: string; // "2026-W01"
size: number; // cohort buyuklugu
retention: number[]; // [100, 45, 32, 28, 25, 23, 22, 21]
}
function buildCohortTable(cohorts: CohortData[]): string[][] {
const header = ["Cohort", "Size", "W0", "W1", "W2", "W3", "W4", "W5", "W6", "W7"];
const rows = cohorts.map(c => [
c.cohort,
String(c.size),
...c.retention.map(r => `${r}%`),
]);
return [header, ...rows];
}
A/B Testing Framework
Experiment Design
interface Experiment {
id: string;
name: string;
hypothesis: string; // "X degisikligi Y metrigini Z kadar arttirir"
primary_metric: string; // tek bir karar metrigi
secondary_metrics: string[];
guardrail_metrics: string[]; // bozulmamasi gereken metrikler
variants: Variant[];
traffic_allocation: number; // %10-50 arasi basla
min_sample_size: number;
min_duration_days: number;
status: "draft" | "running" | "analyzing" | "completed";
}
interface Variant {
id: string;
name: string; // "control" | "treatment_a" | "treatment_b"
weight: number; // 0.5 = %50
description: string;
}
Sample Size Calculator
function calculateSampleSize(
baselineRate: number, // mevcut conversion rate (0.05 = %5)
mde: number, // minimum detectable effect (0.10 = %10 relative)
alpha: number = 0.05, // significance level
power: number = 0.80 // statistical power
): number {
const p1 = baselineRate;
const p2 = baselineRate * (1 + mde);
const zAlpha = 1.96; // two-tailed
const zBeta = 0.84;
const pooledP = (p1 + p2) / 2;
const numerator = Math.pow(
zAlpha * Math.sqrt(2 * pooledP * (1 - pooledP)) +
zBeta * Math.sqrt(p1 * (1 - p1) + p2 * (1 - p2)),
2
);
const denominator = Math.pow(p1 - p2, 2);
return Math.ceil(numerator / denominator);
}
// Ornek: %5 baseline, %10 relative MDE
// calculateSampleSize(0.05, 0.10) => ~31,000 per variant
Statistical Significance Check
function checkSignificance(
controlConversions: number,
controlTotal: number,
treatmentConversions: number,
treatmentTotal: number
): { significant: boolean; pValue: number; lift: number; ci: [number, number] } {
const p1 = controlConversions / controlTotal;
const p2 = treatmentConversions / treatmentTotal;
const pooledP = (controlConversions + treatmentConversions) / (controlTotal + treatmentTotal);
const se = Math.sqrt(pooledP * (1 - pooledP) * (1 / controlTotal + 1 / treatmentTotal));
const z = (p2 - p1) / se;
const pValue = 2 * (1 - normalCDF(Math.abs(z)));
const lift = (p2 - p1) / p1;
const liftSE = Math.sqrt(p2 * (1 - p2) / treatmentTotal + p1 * (1 - p1) / controlTotal) / p1;
const ci: [number, number] = [lift - 1.96 * liftSE, lift + 1.96 * liftSE];
return {
signi