# SaaS Analytics & Metrics **Meta-Description:** SaaS Metriken verstehen und tracken. MRR, Churn, LTV, CAC und Product Analytics für datengetriebenes Wachstum. **Keywords:** SaaS Metrics, MRR, ARR, Churn Rate, LTV, CAC, Product Analytics, Cohort Analysis, Retention --- ## Einführung **SaaS Metriken** sind das Cockpit für Wachstum. Von **MRR** bis **Churn Rate** – die richtigen KPIs messen und verstehen ist entscheidend für fundierte Entscheidungen. Dieser Guide zeigt die wichtigsten Metriken und deren Implementation. --- ## SaaS Metrics Overview ``` ┌─────────────────────────────────────────────────────────────┐ │ SAAS METRICS FRAMEWORK │ ├─────────────────────────────────────────────────────────────┤ │ │ │ Revenue Metrics: │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ MRR (Monthly Recurring Revenue): │ │ │ │ ├── New MRR (New customers) │ │ │ │ ├── Expansion MRR (Upgrades, add-ons) │ │ │ │ ├── Contraction MRR (Downgrades) │ │ │ │ └── Churned MRR (Cancellations) │ │ │ │ │ │ │ │ Net MRR = New + Expansion - Contraction - Churn │ │ │ │ │ │ │ │ ARR = MRR × 12 │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ Growth Metrics: │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ CAC (Customer Acquisition Cost): │ │ │ │ = (Sales + Marketing Costs) / New Customers │ │ │ │ │ │ │ │ LTV (Lifetime Value): │ │ │ │ = ARPU / Churn Rate │ │ │ │ │ │ │ │ LTV:CAC Ratio: │ │ │ │ ├── < 1:1 = Losing money │ │ │ │ ├── 3:1 = Healthy │ │ │ │ └── > 5:1 = Under-investing in growth │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ Retention Metrics: │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ Churn Rate: │ │ │ │ = Customers Lost / Total Customers × 100 │ │ │ │ │ │ │ │ Net Revenue Retention (NRR): │ │ │ │ = (MRR + Expansion - Contraction - Churn) / MRR │ │ │ │ ├── < 100% = Net negative (shrinking) │ │ │ │ ├── 100% = Flat │ │ │ │ └── > 100% = Net positive (growing) │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ Product Metrics: │ │ ├── DAU/MAU (Daily/Monthly Active Users) │ │ ├── Feature Adoption Rate │ │ ├── Time to Value │ │ └── NPS (Net Promoter Score) │ │ │ └─────────────────────────────────────────────────────────────┘ ``` --- ## Metrics Implementation ```typescript // lib/analytics/metrics.ts import { db } from '@/lib/db'; import { startOfMonth, endOfMonth, subMonths, differenceInDays } from 'date-fns'; interface MRRBreakdown { newMRR: number; expansionMRR: number; contractionMRR: number; churnedMRR: number; netMRR: number; totalMRR: number; } export async function calculateMRR(date: Date = new Date()): Promise { const monthStart = startOfMonth(date); const monthEnd = endOfMonth(date); const prevMonthStart = startOfMonth(subMonths(date, 1)); const prevMonthEnd = endOfMonth(subMonths(date, 1)); // Get active subscriptions at end of month const activeSubscriptions = await db.subscription.findMany({ where: { status: 'active', startDate: { lte: monthEnd } }, include: { plan: true } }); const totalMRR = activeSubscriptions.reduce( (sum, sub) => sum + sub.plan.monthlyPrice, 0 ); // New subscriptions this month const newSubscriptions = await db.subscription.findMany({ where: { startDate: { gte: monthStart, lte: monthEnd }, previousSubscriptionId: null }, include: { plan: true } }); const newMRR = newSubscriptions.reduce( (sum, sub) => sum + sub.plan.monthlyPrice, 0 ); // Upgrades this month const upgrades = await db.subscriptionChange.findMany({ where: { type: 'upgrade', changedAt: { gte: monthStart, lte: monthEnd } } }); const expansionMRR = upgrades.reduce( (sum, change) => sum + change.mrrDelta, 0 ); // Downgrades this month const downgrades = await db.subscriptionChange.findMany({ where: { type: 'downgrade', changedAt: { gte: monthStart, lte: monthEnd } } }); const contractionMRR = Math.abs(downgrades.reduce( (sum, change) => sum + change.mrrDelta, 0 )); // Churned this month const churned = await db.subscription.findMany({ where: { status: 'canceled', canceledAt: { gte: monthStart, lte: monthEnd } }, include: { plan: true } }); const churnedMRR = churned.reduce( (sum, sub) => sum + sub.plan.monthlyPrice, 0 ); const netMRR = newMRR + expansionMRR - contractionMRR - churnedMRR; return { newMRR, expansionMRR, contractionMRR, churnedMRR, netMRR, totalMRR }; } export async function calculateChurnRate(date: Date = new Date()): Promise<{ customerChurn: number; revenueChurn: number; netRevenueRetention: number; }> { const monthStart = startOfMonth(date); const prevMonthStart = startOfMonth(subMonths(date, 1)); const prevMonthEnd = endOfMonth(subMonths(date, 1)); // Customers at start of month const customersAtStart = await db.subscription.count({ where: { status: 'active', startDate: { lt: monthStart } } }); // Customers churned this month const customersChurned = await db.subscription.count({ where: { status: 'canceled', canceledAt: { gte: monthStart } } }); const customerChurn = customersAtStart > 0 ? (customersChurned / customersAtStart) * 100 : 0; // MRR calculations for revenue churn const mrrAtStart = await db.subscription.aggregate({ where: { status: 'active', startDate: { lt: monthStart } }, _sum: { monthlyAmount: true } }); const startMRR = mrrAtStart._sum.monthlyAmount || 0; const { churnedMRR, expansionMRR, contractionMRR } = await calculateMRR(date); const revenueChurn = startMRR > 0 ? ((churnedMRR + contractionMRR) / startMRR) * 100 : 0; const netRevenueRetention = startMRR > 0 ? ((startMRR + expansionMRR - contractionMRR - churnedMRR) / startMRR) * 100 : 100; return { customerChurn, revenueChurn, netRevenueRetention }; } export async function calculateLTV(): Promise<{ averageLTV: number; ltv_by_plan: Record; }> { // Average customer lifespan const canceledSubscriptions = await db.subscription.findMany({ where: { status: 'canceled' }, select: { startDate: true, canceledAt: true, monthlyAmount: true, plan: { select: { name: true } } } }); if (canceledSubscriptions.length === 0) { return { averageLTV: 0, ltv_by_plan: {} }; } // Calculate average lifespan and ARPU let totalLifespanMonths = 0; let totalRevenue = 0; const planData: Record = {}; canceledSubscriptions.forEach(sub => { const months = differenceInDays(sub.canceledAt!, sub.startDate) / 30; const revenue = months * sub.monthlyAmount; totalLifespanMonths += months; totalRevenue += revenue; const planName = sub.plan.name; if (!planData[planName]) { planData[planName] = { revenue: 0, months: 0, count: 0 }; } planData[planName].revenue += revenue; planData[planName].months += months; planData[planName].count += 1; }); const averageLTV = totalRevenue / canceledSubscriptions.length; const ltv_by_plan: Record = {}; Object.entries(planData).forEach(([plan, data]) => { ltv_by_plan[plan] = data.revenue / data.count; }); return { averageLTV, ltv_by_plan }; } export async function calculateCAC( startDate: Date, endDate: Date ): Promise<{ totalCAC: number; cacByChannel: Record; }> { // Get marketing and sales costs const costs = await db.marketingCost.aggregate({ where: { date: { gte: startDate, lte: endDate } }, _sum: { amount: true } }); // Get new customers in period const newCustomers = await db.subscription.count({ where: { startDate: { gte: startDate, lte: endDate }, previousSubscriptionId: null } }); const totalCosts = costs._sum.amount || 0; const totalCAC = newCustomers > 0 ? totalCosts / newCustomers : 0; // CAC by channel const costsByChannel = await db.marketingCost.groupBy({ by: ['channel'], where: { date: { gte: startDate, lte: endDate } }, _sum: { amount: true } }); const customersByChannel = await db.subscription.groupBy({ by: ['acquisitionChannel'], where: { startDate: { gte: startDate, lte: endDate }, previousSubscriptionId: null }, _count: true }); const cacByChannel: Record = {}; costsByChannel.forEach(cost => { const customers = customersByChannel.find( c => c.acquisitionChannel === cost.channel )?._count || 0; cacByChannel[cost.channel] = customers > 0 ? (cost._sum.amount || 0) / customers : 0; }); return { totalCAC, cacByChannel }; } ``` --- ## Cohort Analysis ```typescript // lib/analytics/cohorts.ts import { db } from '@/lib/db'; import { startOfMonth, format, differenceInMonths } from 'date-fns'; interface CohortData { cohort: string; // "2024-01" size: number; retention: number[]; // Retention % for each month revenue: number[]; // Revenue for each month } export async function generateRetentionCohorts( months: number = 12 ): Promise { const cohorts: CohortData[] = []; const today = new Date(); for (let i = months - 1; i >= 0; i--) { const cohortDate = startOfMonth(subMonths(today, i)); const cohortKey = format(cohortDate, 'yyyy-MM'); // Get users who signed up in this cohort const cohortUsers = await db.user.findMany({ where: { createdAt: { gte: cohortDate, lt: startOfMonth(subMonths(today, i - 1)) } }, select: { id: true } }); const cohortSize = cohortUsers.length; if (cohortSize === 0) continue; const userIds = cohortUsers.map(u => u.id); const retention: number[] = []; const revenue: number[] = []; // Calculate retention for each subsequent month const monthsSinceCohor = differenceInMonths(today, cohortDate); for (let m = 0; m <= monthsSinceCohor; m++) { const checkMonth = startOfMonth(subMonths(today, monthsSinceCohor - m)); // Count active users in this month const activeUsers = await db.userActivity.groupBy({ by: ['userId'], where: { userId: { in: userIds }, activityDate: { gte: checkMonth, lt: startOfMonth(subMonths(today, monthsSinceCohor - m - 1)) } } }); const retentionRate = (activeUsers.length / cohortSize) * 100; retention.push(Math.round(retentionRate * 10) / 10); // Calculate revenue from this cohort in this month const monthRevenue = await db.payment.aggregate({ where: { userId: { in: userIds }, paidAt: { gte: checkMonth, lt: startOfMonth(subMonths(today, monthsSinceCohor - m - 1)) } }, _sum: { amount: true } }); revenue.push(monthRevenue._sum.amount || 0); } cohorts.push({ cohort: cohortKey, size: cohortSize, retention, revenue }); } return cohorts; } ``` --- ## Analytics Dashboard ```tsx // app/admin/analytics/page.tsx 'use client'; import { useState, useEffect } from 'react'; import { Line, Bar } from 'react-chartjs-2'; interface DashboardMetrics { mrr: { total: number; growth: number; breakdown: { new: number; expansion: number; contraction: number; churn: number; }; }; customers: { total: number; new: number; churned: number; churnRate: number; }; revenue: { ltv: number; cac: number; ltvCacRatio: number; nrr: number; }; mrrHistory: Array<{ month: string; mrr: number }>; } export default function AnalyticsDashboard() { const [metrics, setMetrics] = useState(null); const [dateRange, setDateRange] = useState('12m'); useEffect(() => { fetch(`/api/analytics/dashboard?range=${dateRange}`) .then(res => res.json()) .then(setMetrics); }, [dateRange]); if (!metrics) return
Loading...
; return (

Analytics Dashboard

{/* Key Metrics */}
= 3 ? 'good' : 'warning'} />
{/* MRR Chart */}

MRR Growth

h.month), datasets: [{ label: 'MRR', data: metrics.mrrHistory.map(h => h.mrr), borderColor: '#3b82f6', tension: 0.3 }] }} options={{ scales: { y: { beginAtZero: true, ticks: { callback: (value) => `$${value.toLocaleString()}` } } } }} />
{/* MRR Breakdown */}

MRR Breakdown

Unit Economics

Customer Lifetime Value (LTV) ${metrics.revenue.ltv.toLocaleString()}
Customer Acquisition Cost (CAC) ${metrics.revenue.cac.toLocaleString()}
Net Revenue Retention (NRR) = 100 ? 'text-green-600' : 'text-red-600' }`}> {metrics.revenue.nrr.toFixed(0)}%
); } function MetricCard({ title, value, change, changeLabel, subtitle, status }: { title: string; value: string; change?: number; changeLabel?: string; subtitle?: string; status?: 'good' | 'warning' | 'bad'; }) { return (

{title}

{value} {status && ( )}
{change !== undefined && (

= 0 ? 'text-green-600' : 'text-red-600'}`}> {change >= 0 ? '+' : ''}{change.toFixed(1)}% {changeLabel}

)} {subtitle && (

{subtitle}

)}
); } ``` --- ## Metrics Benchmarks | Metric | Seed/Early | Series A | Scale | |--------|------------|----------|-------| | **MRR Growth** | 15-20%/mo | 10-15%/mo | 5-10%/mo | | **Churn Rate** | < 8% | < 5% | < 3% | | **NRR** | > 90% | > 100% | > 120% | | **LTV:CAC** | > 1:1 | > 3:1 | > 5:1 | | **CAC Payback** | < 18mo | < 12mo | < 8mo | --- ## Fazit SaaS Analytics erfordert: 1. **Revenue Metrics**: MRR, ARR, NRR verstehen 2. **Cohort Analysis**: Retention über Zeit tracken 3. **Unit Economics**: LTV:CAC optimieren 4. **Automation**: Dashboards automatisch aktualisieren Datengetriebene Entscheidungen führen zu nachhaltigem Wachstum. --- ## Bildprompts 1. "SaaS metrics dashboard, MRR chart and key performance indicators" 2. "Cohort retention heatmap, color-coded percentages" 3. "LTV:CAC ratio visualization, balance scale metaphor" --- ## Quellen - [SaaS Metrics 2.0](https://www.forentrepreneurs.com/saas-metrics-2/) - [ChartMogul SaaS Metrics](https://chartmogul.com/blog/saas-metrics/) - [Stripe Atlas Guides](https://stripe.com/atlas/guides) - [OpenView SaaS Benchmarks](https://openviewpartners.com/saas-benchmarks/)