mcpField
P
product-analytics

product-analytics

github.com/travisjneuman数据与数据库106135 次安装今天更新
代码仓库

关于 product-analytics

面向产品与工程团队的产品分析技能,涵盖事件设计、同意管理、漏斗与队列分析及实验,可用于多个指定分析平台。

主要功能

  1. 01类型化事件分类与命名
  2. 02支持同意管理的追踪模式
  3. 03用户识别与身份合并
  4. 04漏斗与队列分析
  5. 05基于功能标志的 A/B 测试

适用场景

  1. 01规划新产品的事件追踪
  2. 02重构不一致的分析事件
  3. 03在追踪前实现用户同意
  4. 04衡量注册或结账流失

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/travisjneuman/.claude --skill product-analytics

安装后在 Claude Code 中输入/product-analytics确认已加载。

详细介绍

产品分析

概述

本技能涵盖产品分析系统的设计与实现,帮助从用户行为中获取可执行的洞察。内容包括事件分类设计、与主流平台(PostHog、Amplitude、Mixpanel、Segment)的分析 SDK 集成、漏斗与群组分析、A/B 测试与功能标志、用户旅程映射、符合 GDPR 要求的同意管理,以及自定义指标定义。

在为新产品添加分析功能、重新设计事件跟踪系统、搭建 A/B 测试基础设施、实现同意管理,或为产品团队构建自定义仪表板时,请使用此技能。


核心原则

  1. 先设计分类体系,再编写代码 - 设计良好的事件命名规范和属性架构,可避免分析工作中最常见的失败:数据不一致、无法查询。先就命名规范达成一致。
  2. 跟踪操作,而不是页面 - 页面浏览能告诉你用户去了哪里;包含上下文的事件能告诉你用户做了什么以及为什么。聚焦于用户操作:project_created、file_uploaded、subscription_upgraded。
  3. 先识别用户,再进行跟踪 - 每个事件都需要用户身份。用户注册前的匿名事件,应在用户登录后关联到其已验证身份(别名/合并)。
  4. 必须征得同意 - GDPR 和 CCPA 要求在跟踪前取得用户的明确同意。应将同意管理纳入分析层,而不是事后补上。
  5. 少即是多 - 跟踪所有内容只会制造噪声。跟踪能够回答具体产品问题的事件即可。之后随时可以添加事件;清理现有数据中的噪声要困难得多。

关键模式

模式 1:事件分类设计

适用场景: 实施任何分析跟踪之前。这是其他所有工作的基础。

实现:

// Event taxonomy schema
// Convention: object_action (noun_verb in past tense)

// Core event types
type AnalyticsEvent =
  // Authentication
  | { event: "user_signed_up"; properties: { method: "email" | "google" | "github"; referralSource?: string } }
  | { event: "user_logged_in"; properties: { method: "email" | "google" | "github" } }
  | { event: "user_logged_out"; properties: Record<string, never> }

  // Onboarding
  | { event: "onboarding_started"; properties: { variant?: string } }
  | { event: "onboarding_step_completed"; properties: { step: number; stepName: string } }
  | { event: "onboarding_completed"; properties: { durationSeconds: number } }
  | { event: "onboarding_skipped"; properties: { lastStep: number } }

  // Core product actions
  | { event: "project_created"; properties: { template?: string; source: "dashboard" | "onboarding" | "api" } }
  | { event: "project_deleted"; properties: { projectAge: number; itemCount: number } }
  | { event: "file_uploaded"; properties: { fileType: string; fileSizeBytes: number; source: "drag_drop" | "file_picker" | "api" } }

  // Subscription
  | { event: "subscription_started"; properties: { plan: string; billingCycle: "monthly" | "annual"; amount: number } }
  | { event: "subscription_upgraded"; properties: { fromPlan: string; toPlan: string } }
  | { event: "subscription_cancelled"; properties: { reason?: string; plan: string; tenureDays: number } }

  // Feature engagement
  | { event: "feature_used"; properties: { feature: string; context: string } }
  | { event: "search_performed"; properties: { query: string; resultCount: number; source: string } }
  | { event: "export_completed"; properties: { format: "csv" | "pdf" | "json"; itemCount: number } };

// Type-safe tracking function
function track<T extends AnalyticsEvent>(
  event: T["event"],
  properties: T["properties"]
): void {
  // Implementation below
}

// Usage - fully typed, autocomplete works
track("project_created", {
  template: "blank",
  source: "dashboard",
});

原因: 类型化的事件分类体系可避免拼写错误(user_signedup 与 user_signed_up),确保始终包含必需属性,并让跟踪计划能够自我说明。object_action 约定可在分析仪表板中将相关事件归为一组。


模式 2:带同意管理的分析客户端

适用场景: 所有跟踪用户行为的产品,也就是所有产品。

实现:

// analytics.ts - Unified analytics client
import posthog from "posthog-js";

type ConsentStatus = "granted" | "denied" | "pending";

interface AnalyticsConfig {
  posthogKey: string;
  posthogHost?: string;
}

class Analytics {
  private initialized = false;
  private consent: ConsentStatus = "pending";
  private queuedEvents: Array<{ event: string; properties: Record<string, unknown> }> = [];

  init(config: AnalyticsConfig) {
    posthog.init(config.posthogKey, {
      api_host: config.posthogHost ?? "https://us.i.posthog.com",
      persistence: "localStorage+cookie",
      autocapture: false, // Explicit tracking only
      capture_pageview: false, // Manual pageview tracking
      capture_pageleave: true,
      // Respect Do Not Track
      respect_dnt: true,
      // Cookie-less mode until consent
      persistence: this.consent === "granted" ? "localStorage+cookie" : "memory",
    });
    this.initialized = true;
  }

  setConsent(status: ConsentStatus) {
    this.consent = status;

    if (status === "granted") {
      posthog.opt_in_capturing();
      // Flush queued events
      for (const event of this.queuedEvents) {
        this.trackInternal(event.event, event.properties);
      }
      this.queuedEvents = [];
    } else if (status === "denied") {
      posthog.opt_out_capturing();
      this.queuedEvents = [];
    }
  }

  identify(userId: string, traits?: Record<string, unknown>) {
    if (this.consent !== "granted") return;
    posthog.identify(userId, traits);
  }

  // Alias anonymous ID to authenticated ID (for signup flow)
  alias(newId: string) {
    if (this.consent !== "granted") return;
    posthog.alias(newId);
  }

  track(event: string, properties?: Record<string, unknown>) {
    if (this.consent === "denied") return;

    const enrichedProperties = {
      ...properties,
      timestamp: new Date().toISOString(),
      url: typeof window !== "undefined" ? window.location.href : undefined,
      referrer: typeof document !== "undefined" ? document.referrer : undefined,
    };

    if (this.consent === "pending") {
      this.queuedEvents.push({ event, properties: enrichedProperties });
      return;
    }

    this.trackInternal(event, enrichedProperties);
  }

  private trackInternal(event: string, properties: Record<string, unknown>) {
    if (!this.initialized) return;
    posthog.capture(event, properties);
  }

  page(name?: string, properties?: Record<string, unknown>) {
    this.track("$pageview", { pageName: name, ...properties });
  }

  reset() {
    posthog.reset();
  }
}

export const analytics = new Analytics();
// React consent banner component
function ConsentBanner() {
  const [showBanner, setShowBanner] = useState(() => {
    return localStorage.getItem("analytics_consent") === null;
  });

  const handleConsent = (granted: boolean) => {
    const status = granted ? "granted" : "denied";
    localStorage.setItem("analytics_consent", status);
    analytics.setConsent(status);
    setShowBanner(false);
  };

  if (!showBanner) return null;

  return (
    <div role="dialog" aria-label="Cookie consent" className="consent-banner">
      <p>We use analytics to improve our product. No personal data is sold.</p>
      <div className="consent-actions">
        <button onClick={() => handleConsent(false)}>Decline</button>
        <button onClick={() => handleConsent(true)}>Accept</button>
      </div>
    </div>
  );
}

原因: 先征得同意再进行分析是法律要求(GDPR、CCPA),也有助于建立用户信任。队列模式可确保用户在执行操作后才授予同意时,事件也不会丢失。明确跟踪(不自动捕获)能让数据保持干净、符合预期。


模式 3:漏斗分析实现

适用场景: 衡量用户在多步骤流程(注册、引导、结账)中的转化情况。

实现:

// Track each funnel step explicitly
const ONBOARDING_FUNNEL = [
  "onboarding_started",
  "onboarding_step_completed:profile",
  "onboarding_step_completed:workspace",
  "onboarding_step_completed:invite",
  "onboarding_completed",
] as const;

// Instrument each step
function OnboardingFlow() {
  const [currentStep, setCurrentStep] = useState(0);

  useEffect(() => {
    analytics.track("onboarding_started", { variant: "v2" });
  }, []);

  const completeStep = (stepName: string) => {
    analytics.track("onboarding_step_completed", {
      step: currentStep,
      stepName,
      timeOnStepSeconds: getTimeOnStep(),
    });
    setCurrentStep((prev) => prev + 1);
  };

  const skip = () => {
    analytics.track("onboarding_skipped", {
      lastStep: currentStep,
      lastStepName: steps[currentStep].name,
    });
    router.push("/dashboard");
  };

  const complete = () => {
    analytics.track("onboarding_completed", {
      durationSeconds: getTotalDuration(),
      stepsCompleted: currentStep + 1,
      stepsSkipped: steps.length - currentStep - 1,
    });
    router.push("/dashboard");
  };

  // ...render steps
}

// Checkout funnel tracking
function useCheckoutFunnel() {
  const trackStep = (step: string, properties?: Record<string, unknown>) => {
    analytics.track(`checkout_${step}`, {
      ...properties,
      cartValue: getCartTotal(),
      itemCount: getCartItemCount(),
      timestamp: Date.now(),
    });
  };

  return {
    viewCart: () => trackStep("cart_viewed"),
    startCheckout: () => trackStep("started"),
    addShipping: (method: string) => trackStep("shipping_added", { method }),
    addPayment: (type: string) => trackStep("payment_added", { type }),
    complete: (orderId: string) => trackStep("completed", { orderId }),
    abandon: (step: string) => trackStep("abandoned", { lastStep: step }),
  };
}

原因: 漏斗分析能揭示用户在哪一步流失。结合上下文跟踪每个步骤(耗时、变体、购物车金额),即可进行细分分析:“来自 Google Ads 的用户在配送步骤的流失率是自然流量用户的 3 倍。”这一洞察有助于开展有针对性的优化。


模式 4:由功能标志驱动的 A/B 测试

适用场景: 在向所有用户推出产品变更之前,以严谨的统计方法进行测试。

实现:

// PostHog feature flags for A/B testing
import posthog from "posthog-js";

// Check feature flag with typed variants
function useExperiment<T extends string>(
  flagKey: string,
  variants: T[]
): T | "control" | undefined {
  const [variant, setVariant] = useState<T | "control" | undefined>();

  useEffect(() => {
    posthog.onFeatureFlags(() => {
      const value = posthog.getFeatureFlag(flagKey);

      if (value === false || value === undefined) {
        setVariant("control");
      } else if (typeof value === "string" && variants.includes(value as T)) {
        setVariant(value as T);
      } else {
        setVariant("control");
      }

      // Track experiment exposure (for accurate analysis)
      analytics.track("$experiment_started", {
        experiment: flagKey,
        variant: value ?? "control",
      });
    });
  }, [flagKey]);

  return variant;
}

// Usage in component
function PricingPage() {
  const variant = useExperiment("pricing-page-redesign", ["new-layout", "social-proof"]);

  if (!variant) return <LoadingSkeleton />;

  switch (variant) {
    case "new-layout":
      return <PricingNewLayout />;
    case "social-proof":
      return <PricingSocialProof />;
    case "control":
    default:
      return <PricingControl />;
  }
}

原因: A/B 测试可让产品决策摆脱主观判断。功能标志支持逐步推出、即时回滚和针对特定细分群体进行定向。将曝光事件与转化事件分开跟踪,可确保统计分析准确(意向治疗分析)。


事件命名规范参考

规范示例适用场景
object_actionproject_created、file_uploaded标准产品事件
$system_event$pageview、$identify系统/平台事件
experiment_*$experiment_startedA/B 测试跟踪
feature_usedfeature_used { feature: "export" }通用功能使用情况

反模式

反模式问题所在更好的做法
自动捕获所有数据数据嘈杂、难以查询,且存在隐私风险使用定义明确的分类体系显式跟踪事件
事件名称不一致userSignedUp、user_signed_up 和 signup 混用使用 TypeScript 类型强制执行命名规范
未经同意就跟踪违反 GDPR/CCPA,罚款最高可达收入的 4%使用带队列模式的同意横幅
未识别用户身份无法进行群组分析或留存分析用户登录时识别用户,注册时设置别名
在事件属性中跟踪 PII侵犯隐私,导致数据保留问题使用用户 ID,不要在事件属性中使用电子邮件地址或姓名
使用一个庞大的 "user_action" 事件无法创建有意义的漏斗为每个有意义的操作设置专属事件
未跟踪负面结果只能看到哪些做法有效,看不到哪些无效跟踪错误、放弃行为和愤怒点击

检查清单

  • 已记录事件分类体系并定义类型(TypeScript 接口)
  • 已强制执行命名规范(object_action 模式)
  • 已初始化并集成同意管理的分析客户端
  • 用户登录时识别用户,注册时设置别名
  • 已使用上下文属性跟踪漏斗步骤
  • 已设置功能标志以进行 A/B 测试
  • 分别跟踪实验曝光事件和转化事件
  • 已使用队列模式实现 GDPR 同意横幅
  • 事件属性中不包含 PII(电子邮件地址、全名、IP)
  • 已在分析仪表板中可视化关键漏斗
  • 已配置留存群组(第 1 天、第 7 天、第 30 天)
  • 已在开发环境中验证分析事件(调试模式)

相关资源

  • 技能: growth-engineering(A/B 测试基础设施)、authentication-patterns(识别用户/设置别名流程)
  • 技能: accessibility-a11y(无障碍同意横幅)
  • 规则: docs/reference/stacks/react-typescript.md(用于分析的 React Hooks)

评论

支持 Markdown