diff --git a/product-team/product-analytics/SKILL.md b/product-team/product-analytics/SKILL.md new file mode 100644 index 0000000..8ea35ce --- /dev/null +++ b/product-team/product-analytics/SKILL.md @@ -0,0 +1,107 @@ +--- +name: product-analytics +description: Use when defining product KPIs, building metric dashboards, running cohort or retention analysis, or interpreting feature adoption trends across product stages. +--- + +# Product Analytics + +Define, track, and interpret product metrics across discovery, growth, and mature product stages. + +## When To Use + +Use this skill for: +- Metric framework selection (AARRR, North Star, HEART) +- KPI definition by product stage (pre-PMF, growth, mature) +- Dashboard design and metric hierarchy +- Cohort and retention analysis +- Feature adoption and funnel interpretation + +## Workflow + +1. Select metric framework +- AARRR for growth loops and funnel visibility +- North Star for cross-functional strategic alignment +- HEART for UX quality and user experience measurement + +2. Define stage-appropriate KPIs +- Pre-PMF: activation, early retention, qualitative success +- Growth: acquisition efficiency, expansion, conversion velocity +- Mature: retention depth, revenue quality, operational efficiency + +3. Design dashboard layers +- Executive layer: 5-7 directional metrics +- Product health layer: acquisition, activation, retention, engagement +- Feature layer: adoption, depth, repeat usage, outcome correlation + +4. Run cohort + retention analysis +- Segment by signup cohort or feature exposure cohort +- Compare retention curves, not single-point snapshots +- Identify inflection points around onboarding and first value moment + +5. Interpret and act +- Connect metric movement to product changes and release timeline +- Distinguish signal from noise using period-over-period context +- Propose one clear product action per major metric risk/opportunity + +## KPI Guidance By Stage + +### Pre-PMF +- Activation rate +- Week-1 retention +- Time-to-first-value +- Problem-solution fit interview score + +### Growth +- Funnel conversion by stage +- Monthly retained users +- Feature adoption among new cohorts +- Expansion / upsell proxy metrics + +### Mature +- Net revenue retention aligned product metrics +- Power-user share and depth of use +- Churn risk indicators by segment +- Reliability and support-deflection product metrics + +## Dashboard Design Principles + +- Show trends, not isolated point estimates. +- Keep one owner per KPI. +- Pair each KPI with target, threshold, and decision rule. +- Use cohort and segment filters by default. +- Prefer comparable time windows (weekly vs weekly, monthly vs monthly). + +See: +- `references/metrics-frameworks.md` +- `references/dashboard-templates.md` + +## Cohort Analysis Method + +1. Define cohort anchor event (signup, activation, first purchase). +2. Define retained behavior (active day, key action, repeat session). +3. Build retention matrix by cohort week/month and age period. +4. Compare curve shape across cohorts. +5. Flag early drop points and investigate journey friction. + +## Retention Curve Interpretation + +- Sharp early drop, low plateau: onboarding mismatch or weak initial value. +- Moderate drop, stable plateau: healthy core audience with predictable churn. +- Flattening at low level: product used occasionally, revisit value metric. +- Improving newer cohorts: onboarding or positioning improvements are working. + +## Tooling + +### `scripts/metrics_calculator.py` + +CLI utility for: +- Retention rate calculations by cohort age +- Cohort table generation +- Basic funnel conversion analysis + +Examples: +```bash +python3 scripts/metrics_calculator.py retention events.csv +python3 scripts/metrics_calculator.py cohort events.csv --cohort-grain month +python3 scripts/metrics_calculator.py funnel funnel.csv --stages visit,signup,activate,pay +``` diff --git a/product-team/product-analytics/references/dashboard-templates.md b/product-team/product-analytics/references/dashboard-templates.md new file mode 100644 index 0000000..2b94d33 --- /dev/null +++ b/product-team/product-analytics/references/dashboard-templates.md @@ -0,0 +1,66 @@ +# Dashboard Templates + +## 1. Executive Dashboard Template + +Purpose: quick company-level product signal for leadership. + +Sections: +1. North Star trend (current, target, trailing 12 periods) +2. Growth summary (new users/accounts, activation) +3. Retention summary (short-term + medium-term cohorts) +4. Revenue-linked product indicators +5. Risks and actions + +Suggested KPI block: + +| KPI | Current | Target | Delta | Owner | Action | +|---|---:|---:|---:|---|---| +| North Star | | | | | | +| Activation Rate | | | | | | +| W8 Retention | | | | | | +| Paid Conversion | | | | | | + +## 2. Product Health Dashboard Template + +Purpose: monitor full user journey and detect bottlenecks. + +Sections: +1. Acquisition funnel by channel/segment +2. Activation funnel with drop-off points +3. Cohort retention matrix + curve chart +4. Feature adoption distribution +5. Reliability metrics tied to user outcomes + +Recommended views: +- Weekly cohort retention heatmap +- Funnel stage conversion waterfall +- Segment comparison (SMB vs enterprise) +- New vs returning user behavior split + +## 3. Feature Adoption Dashboard Template + +Purpose: evaluate feature launch quality and ongoing usage. + +Sections: +1. Exposure and eligibility count +2. First-use adoption rate +3. Repeat usage rate (2nd, 3rd, nth use) +4. Time-to-adoption from signup/activation +5. Impact on primary outcomes (retention, conversion) + +Adoption KPI examples: + +| Metric | Definition | +|---|---| +| First-use adoption | Users who used feature at least once / eligible users | +| Repeat adoption | Users with 2+ uses / users with first use | +| Sustained adoption | Users with usage in 3 of last 4 weeks | +| Time to adoption | Median days from eligibility to first use | + +## Dashboard Design Rules + +- Keep each dashboard to one decision horizon (weekly ops vs quarterly strategy). +- Always annotate major product releases on charts. +- Add threshold bands for risk detection. +- Show metric definitions next to charts. +- Include a short "what changed" narrative block. diff --git a/product-team/product-analytics/references/metrics-frameworks.md b/product-team/product-analytics/references/metrics-frameworks.md new file mode 100644 index 0000000..dc23cca --- /dev/null +++ b/product-team/product-analytics/references/metrics-frameworks.md @@ -0,0 +1,84 @@ +# Metrics Frameworks + +## AARRR (Pirate Metrics) + +AARRR breaks the product journey into five stages. + +1. Acquisition +- How users discover the product +- Example metrics: signups, CAC, channel conversion + +2. Activation +- First meaningful value moment +- Example metrics: activation rate, time-to-first-value + +3. Retention +- Ongoing user return behavior +- Example metrics: D7/W4 retention, rolling retained users + +4. Revenue +- Monetization and value capture +- Example metrics: conversion to paid, ARPU, expansion revenue + +5. Referral +- Organic growth from existing users +- Example metrics: referral rate, invite conversion, K-factor + +## North Star Metric Framework + +North Star = metric capturing long-term customer value delivered. + +### North Star Criteria +- Reflects real user value +- Sensitive to product improvements +- Predictive of sustainable growth +- Understandable across functions + +### Example North Star Metrics +- Collaboration SaaS: weekly active teams +- Marketplace: successful transactions per active buyer +- Content product: hours of qualified consumption + +### Input Metrics +Track levers that influence the North Star: +- Acquisition quality +- Activation quality +- Engagement depth +- Retention durability + +## HEART Framework + +HEART is a UX-oriented framework from Google. + +- Happiness: satisfaction, NPS, perceived quality +- Engagement: interaction depth/frequency +- Adoption: first-time use of features/products +- Retention: return behavior over time +- Task Success: completion rate, error rate, time on task + +### HEART + Goals-Signals-Metrics +1. Goals: what UX outcome you want +2. Signals: observed behavior indicating movement +3. Metrics: measurable indicator for each signal + +## Framework Selection Guide + +| Situation | Recommended Framework | +|---|---| +| Early growth and funnel bottlenecks | AARRR | +| Company-wide strategic alignment | North Star | +| UX and product quality optimization | HEART | +| Mixed maturity org | North Star + AARRR operational layers | + +## Example: B2B SaaS Product + +- North Star: weekly active accounts completing core workflow +- AARRR operational metrics: + - Acquisition: qualified signups + - Activation: % accounts completing setup in 7 days + - Retention: W8 retained accounts + - Revenue: paid conversion and expansion rate + - Referral: invited teammate activation rate +- HEART for onboarding redesign: + - Task Success: onboarding completion rate + - Happiness: onboarding CSAT diff --git a/product-team/product-analytics/scripts/metrics_calculator.py b/product-team/product-analytics/scripts/metrics_calculator.py new file mode 100755 index 0000000..5f9087d --- /dev/null +++ b/product-team/product-analytics/scripts/metrics_calculator.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python3 +"""Product metrics calculator: retention, cohort matrix, and funnel conversion.""" + +import argparse +import csv +import datetime as dt +from collections import defaultdict + + +def parse_date(value: str) -> dt.date: + return dt.date.fromisoformat(value.strip()[:10]) + + +def load_csv(path: str): + with open(path, "r", encoding="utf-8", newline="") as handle: + return list(csv.DictReader(handle)) + + +def retention(args: argparse.Namespace) -> int: + rows = load_csv(args.input) + cohorts = {} + activity = defaultdict(set) + + for row in rows: + user = row[args.user_column].strip() + cohort_date = parse_date(row[args.cohort_column]) + activity_date = parse_date(row[args.activity_column]) + cohorts[user] = min(cohorts.get(user, cohort_date), cohort_date) + delta = (activity_date - cohorts[user]).days + if delta >= 0: + activity[delta].add(user) + + base_users = len(cohorts) + if base_users == 0: + print("No users found.") + return 1 + + print("Retention by period") + print("period,active_users,retention_rate") + max_period = args.max_period + for period in range(0, max_period + 1): + users = len(activity.get(period, set())) + rate = users / base_users + print(f"{period},{users},{rate:.4f}") + return 0 + + +def cohort(args: argparse.Namespace) -> int: + rows = load_csv(args.input) + cohorts = {} + activity = defaultdict(set) + + for row in rows: + user = row[args.user_column].strip() + cohort_date = parse_date(row[args.cohort_column]) + activity_date = parse_date(row[args.activity_column]) + + if args.cohort_grain == "month": + cohort_key = cohort_date.strftime("%Y-%m") + else: + cohort_key = f"{cohort_date.isocalendar().year}-W{cohort_date.isocalendar().week:02d}" + + cohorts.setdefault(user, cohort_key) + age = (activity_date - cohort_date).days + if age >= 0: + activity[(cohort_key, age)].add(user) + + cohort_sizes = defaultdict(int) + for cohort_key in cohorts.values(): + cohort_sizes[cohort_key] += 1 + + cohort_keys = sorted(cohort_sizes.keys()) + print("cohort,age_days,active_users,cohort_size,retention_rate") + for cohort_key in cohort_keys: + size = cohort_sizes[cohort_key] + for age in range(0, args.max_period + 1): + active_users = len(activity.get((cohort_key, age), set())) + rate = (active_users / size) if size else 0 + print(f"{cohort_key},{age},{active_users},{size},{rate:.4f}") + return 0 + + +def funnel(args: argparse.Namespace) -> int: + rows = load_csv(args.input) + stages = [item.strip() for item in args.stages.split(",") if item.strip()] + if not stages: + print("No stages provided.") + return 1 + + stage_users = {stage: set() for stage in stages} + for row in rows: + user = row[args.user_column].strip() + stage = row[args.stage_column].strip() + if stage in stage_users: + stage_users[stage].add(user) + + print("stage,users,conversion_from_previous,conversion_from_first") + previous_count = None + first_count = None + for stage in stages: + count = len(stage_users[stage]) + if first_count is None: + first_count = count + conv_prev = (count / previous_count) if previous_count else 1.0 + conv_first = (count / first_count) if first_count else 0 + print(f"{stage},{count},{conv_prev:.4f},{conv_first:.4f}") + previous_count = count + return 0 + + +def build_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser( + description="Calculate retention, cohort, and funnel metrics from CSV data." + ) + subparsers = parser.add_subparsers(dest="command", required=True) + + common = { + "help": "CSV input path", + } + + retention_parser = subparsers.add_parser("retention", help="Calculate retention by day.") + retention_parser.add_argument("input", **common) + retention_parser.add_argument("--user-column", default="user_id") + retention_parser.add_argument("--cohort-column", default="cohort_date") + retention_parser.add_argument("--activity-column", default="activity_date") + retention_parser.add_argument("--max-period", type=int, default=30) + retention_parser.set_defaults(func=retention) + + cohort_parser = subparsers.add_parser("cohort", help="Build cohort retention matrix rows.") + cohort_parser.add_argument("input", **common) + cohort_parser.add_argument("--user-column", default="user_id") + cohort_parser.add_argument("--cohort-column", default="cohort_date") + cohort_parser.add_argument("--activity-column", default="activity_date") + cohort_parser.add_argument("--cohort-grain", choices=["week", "month"], default="week") + cohort_parser.add_argument("--max-period", type=int, default=30) + cohort_parser.set_defaults(func=cohort) + + funnel_parser = subparsers.add_parser("funnel", help="Calculate funnel conversion by stage.") + funnel_parser.add_argument("input", **common) + funnel_parser.add_argument("--user-column", default="user_id") + funnel_parser.add_argument("--stage-column", default="stage") + funnel_parser.add_argument("--stages", required=True) + funnel_parser.set_defaults(func=funnel) + + return parser + + +def main() -> int: + parser = build_parser() + args = parser.parse_args() + return args.func(args) + + +if __name__ == "__main__": + raise SystemExit(main())