Skills Development PostHog Analytics Reference Architecture

PostHog Analytics Reference Architecture

v20260423
posthog-reference-architecture
This guide provides a comprehensive, production-grade architecture for implementing analytics using PostHog. It covers best practices for event taxonomy design, SDK layering (client and server), feature flag management, and integrating the data pipeline (CDP). Ideal for tracking user lifecycle, feature adoption, and revenue in complex web applications.
Get Skill
445 downloads
Overview

PostHog Reference Architecture

Overview

Production-grade architecture for PostHog analytics in a web application. Covers file structure, event taxonomy design, SDK initialization layers, feature flag management, group analytics for B2B, and data pipeline integration.

Prerequisites

  • PostHog Cloud or self-hosted instance
  • posthog-js and posthog-node SDKs
  • Next.js or React application (patterns adapt to other frameworks)

Architecture

┌─────────────────────────────────────────────────────┐
│  Browser (posthog-js)                                │
│  $pageview, $autocapture, custom events, identify   │
│  Feature flag evaluation, session recordings         │
└────────────┬────────────────────────────────────────┘
             │ HTTPS (direct or reverse proxy)
             ▼
┌─────────────────────────────────────────────────────┐
│  PostHog Cloud (us.i.posthog.com)                    │
│  ┌──────────┐  ┌──────────┐  ┌───────────────────┐  │
│  │ Events   │  │ Feature  │  │ Session Replay    │  │
│  │ Pipeline │  │ Flags    │  │ & Recordings      │  │
│  └────┬─────┘  └────┬─────┘  └───────────────────┘  │
│       │              │                                │
│  ┌────┴──────────────┴────────────────────────────┐  │
│  │  Analytics: Trends, Funnels, Retention, Paths  │  │
│  │  HogQL (SQL), Dashboards, Cohorts              │  │
│  └────────────────────────────────────────────────┘  │
│  ┌────────────────────────────────────────────────┐  │
│  │  CDP: Destinations (Webhook, Slack, S3, etc.)  │  │
│  └────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────┘
             ▲
             │ posthog-node (server events, local flag eval)
┌────────────┴────────────────────────────────────────┐
│  Backend (API routes, webhooks, crons)               │
│  Server-side capture, group identify, flag eval      │
└─────────────────────────────────────────────────────┘

Instructions

Step 1: Project File Structure

src/
├── analytics/
│   ├── posthog.ts           # Browser SDK init (singleton)
│   ├── posthog-server.ts    # Server SDK init (singleton)
│   ├── events.ts            # Typed event constants
│   ├── flags.ts             # Feature flag key constants
│   └── hooks/
│       ├── useFeatureFlag.ts    # React hook for boolean flags
│       └── useExperiment.ts     # React hook for A/B variants
├── app/
│   ├── providers.tsx        # PostHogProvider wrapper
│   └── layout.tsx           # Root layout with provider
└── lib/
    └── analytics.ts         # High-level tracking functions

Step 2: Event Taxonomy

// analytics/events.ts
// Naming convention: object_action (noun_verb)
export const EVENTS = {
  // User lifecycle (track conversion funnel)
  USER_SIGNED_UP: 'user_signed_up',
  USER_LOGGED_IN: 'user_logged_in',
  USER_ONBOARDING_COMPLETED: 'user_onboarding_completed',
  USER_INVITED_TEAMMATE: 'user_invited_teammate',

  // Core product (track feature adoption)
  FEATURE_USED: 'feature_used',           // with feature_name property
  ITEM_CREATED: 'item_created',
  ITEM_UPDATED: 'item_updated',
  ITEM_DELETED: 'item_deleted',
  SEARCH_PERFORMED: 'search_performed',
  EXPORT_COMPLETED: 'export_completed',

  // Revenue (track MRR and churn)
  SUBSCRIPTION_STARTED: 'subscription_started',
  SUBSCRIPTION_UPGRADED: 'subscription_upgraded',
  SUBSCRIPTION_DOWNGRADED: 'subscription_downgraded',
  SUBSCRIPTION_CANCELED: 'subscription_canceled',
  PAYMENT_COMPLETED: 'payment_completed',

  // Engagement (track stickiness)
  NOTIFICATION_CLICKED: 'notification_clicked',
  FEEDBACK_SUBMITTED: 'feedback_submitted',
} as const;

// Standard property schema
interface BaseProps {
  source?: 'web' | 'mobile' | 'api';
  plan?: 'free' | 'pro' | 'enterprise';
}

// Type-safe capture
type EventMap = {
  [EVENTS.USER_SIGNED_UP]: BaseProps & { method: 'email' | 'google' | 'github' };
  [EVENTS.FEATURE_USED]: BaseProps & { feature_name: string; duration_ms?: number };
  [EVENTS.SUBSCRIPTION_STARTED]: BaseProps & { plan: string; interval: 'monthly' | 'annual'; mrr: number };
};

Step 3: Feature Flag Constants

// analytics/flags.ts
export const FLAGS = {
  // Feature rollouts
  NEW_DASHBOARD: 'new-dashboard-v2',
  AI_SUMMARIZE: 'ai-summarize-beta',
  BULK_EXPORT: 'bulk-export',

  // Experiments
  PRICING_PAGE: 'pricing-page-experiment',
  ONBOARDING_FLOW: 'onboarding-flow-v3',
  CHECKOUT_LAYOUT: 'checkout-layout-test',
} as const;

// Flag → default value mapping (used when flags fail to load)
export const FLAG_DEFAULTS: Record<string, boolean | string> = {
  [FLAGS.NEW_DASHBOARD]: false,
  [FLAGS.AI_SUMMARIZE]: false,
  [FLAGS.PRICING_PAGE]: 'control',
  [FLAGS.ONBOARDING_FLOW]: 'control',
};

Step 4: High-Level Analytics Module

// lib/analytics.ts
import posthog from 'posthog-js';
import { getPostHogServer } from '../analytics/posthog-server';
import { EVENTS } from '../analytics/events';

// Client-side tracking
export function trackFeatureUsed(featureName: string, duration?: number) {
  posthog.capture(EVENTS.FEATURE_USED, {
    feature_name: featureName,
    duration_ms: duration,
    source: 'web',
  });
}

export function trackSignup(method: 'email' | 'google' | 'github') {
  posthog.capture(EVENTS.USER_SIGNED_UP, { method, source: 'web' });
}

export function identifyUser(userId: string, properties: {
  email: string;
  name: string;
  plan: string;
  companyId?: string;
  companyName?: string;
}) {
  posthog.identify(userId, {
    email: properties.email,
    name: properties.name,
    plan: properties.plan,
  });

  if (properties.companyId) {
    posthog.group('company', properties.companyId, {
      name: properties.companyName,
      plan: properties.plan,
    });
  }
}

// Server-side tracking
export function trackServerEvent(
  userId: string,
  event: string,
  properties?: Record<string, any>
) {
  const ph = getPostHogServer();
  ph.capture({
    distinctId: userId,
    event,
    properties: { ...properties, source: 'api' },
  });
}

Step 5: Data Pipeline Integration

// PostHog → External Systems via CDP Destinations
//
// PostHog Cloud Data Pipeline:
// 1. Events captured → PostHog stores in ClickHouse
// 2. CDP Destinations fire webhooks to your endpoints
// 3. HogQL queries available for custom analysis
//
// Common destination patterns:
// - PostHog → Webhook → Your API → CRM sync
// - PostHog → S3 export → Data warehouse
// - PostHog → Slack → Team notifications
// - PostHog → Webhook → Billing system (revenue events)

// Server route to receive PostHog CDP webhooks
export async function handlePostHogWebhook(event: string, payload: any) {
  switch (event) {
    case EVENTS.SUBSCRIPTION_STARTED:
      await syncToStripe(payload);
      break;
    case EVENTS.USER_SIGNED_UP:
      await syncToCRM(payload);
      await notifySlack(payload);
      break;
  }
}

Error Handling

IssueCauseSolution
Events not appearingSDK not initializedVerify posthog.init() runs before capture
Flag always returns defaultFlags not loadedUse posthog.onFeatureFlags() callback
Identity fragmentationInconsistent distinct_idUse same user ID from auth system everywhere
Group analytics emptyposthog.group() not calledCall group() before capture
Server events lostNo flush() in serverlessAlways await posthog.shutdown()

Output

  • Organized analytics module with typed events and flags
  • Client and server SDK initialization (singleton pattern)
  • Event taxonomy following object_action naming convention
  • Feature flag constants with safe defaults
  • Data pipeline integration via CDP webhooks

Resources

Info
Category Development
Name posthog-reference-architecture
Version v20260423
Size 9.94KB
Updated At 2026-04-28
Language