Claude-code-plugins-plus-skills posthog-performance-tuning
install
source · Clone the upstream repo
git clone https://github.com/jeremylongshore/claude-code-plugins-plus-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/jeremylongshore/claude-code-plugins-plus-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/saas-packs/posthog-pack/skills/posthog-performance-tuning" ~/.claude/skills/jeremylongshore-claude-code-plugins-plus-skills-posthog-performance-tuning && rm -rf "$T"
manifest:
plugins/saas-packs/posthog-pack/skills/posthog-performance-tuning/SKILL.mdsource content
PostHog Performance Tuning
Overview
Optimize PostHog for production workloads. The biggest performance wins are: local feature flag evaluation (eliminates network calls), proper batching configuration, event sampling for high-volume apps, and efficient HogQL queries with date filters.
Prerequisites
and/orposthog-node
installedposthog-js- Personal API key (
) for local flag evaluationphx_... - Feature flags configured (if applicable)
Instructions
Step 1: Enable Local Feature Flag Evaluation
The single biggest performance improvement. Without local evaluation, every
getFeatureFlag() call makes a network request (~50-200ms). With local evaluation, flag definitions are cached and evaluation is instant (~0.1ms).
import { PostHog } from 'posthog-node'; const posthog = new PostHog(process.env.NEXT_PUBLIC_POSTHOG_KEY!, { host: 'https://us.i.posthog.com', // This is the key: personal API key enables local flag evaluation personalApiKey: process.env.POSTHOG_PERSONAL_API_KEY, // Flag definitions are polled every 30 seconds by default // Adjust if you need faster flag updates: // featureFlagsPollingInterval: 10000, // 10 seconds }); // With personalApiKey set, this evaluates locally (no network call) const variant = await posthog.getFeatureFlag('pricing-experiment', 'user-123', { personProperties: { plan: 'pro', country: 'US' }, }); // Get all flags at once (still local, still fast) const allFlags = await posthog.getAllFlags('user-123', { personProperties: { plan: 'pro' }, groupProperties: { company: { industry: 'SaaS' } }, });
Step 2: Optimize Client Batching
// Production: batch events for network efficiency const posthog = new PostHog(process.env.NEXT_PUBLIC_POSTHOG_KEY!, { host: 'https://us.i.posthog.com', flushAt: 20, // Send batch when 20 events accumulated (default) flushInterval: 10000, // Or flush every 10 seconds (default) requestTimeout: 10000, // 10 second timeout per request maxRetries: 3, // Retry failed sends }); // Serverless: flush immediately (function may exit) const serverless = new PostHog(process.env.NEXT_PUBLIC_POSTHOG_KEY!, { host: 'https://us.i.posthog.com', flushAt: 1, // Send every event immediately flushInterval: 0, // Don't wait }); // CRITICAL: Always shutdown before process exits process.on('SIGTERM', async () => { await posthog.shutdown(); process.exit(0); });
Step 3: Event Sampling (Browser)
import posthog from 'posthog-js'; posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, { api_host: 'https://us.i.posthog.com', before_send: (event) => { // Always capture business-critical events const alwaysCapture = ['purchase', 'signup', 'subscription_started', 'subscription_canceled']; if (alwaysCapture.includes(event.event)) return event; // Sample high-volume events const sampleRates: Record<string, number> = { '$pageview': 1.0, // Keep all pageviews '$pageleave': 0.5, // Sample 50% '$autocapture': 0.1, // Sample 10% of autocapture 'scroll_depth': 0.05, // Sample 5% }; const rate = sampleRates[event.event] ?? 0.5; if (Math.random() >= rate) return null; // Drop event // Tag sampled events so you can adjust in analysis event.properties = { ...event.properties, $sample_rate: rate }; return event; }, });
Step 4: Efficient HogQL Queries
async function queryPostHog(hogql: string) { const response = await fetch( `https://app.posthog.com/api/projects/${process.env.POSTHOG_PROJECT_ID}/query/`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${process.env.POSTHOG_PERSONAL_API_KEY}`, }, body: JSON.stringify({ query: { kind: 'HogQLQuery', query: hogql }, }), } ); return response.json(); } // FAST: Filtered by time, limited results const fast = await queryPostHog(` SELECT properties.$current_url AS url, count() AS views, uniq(distinct_id) AS visitors FROM events WHERE event = '$pageview' AND timestamp > now() - interval 7 day GROUP BY url ORDER BY views DESC LIMIT 50 `); // SLOW (avoid): No time filter, scans entire table // SELECT * FROM events WHERE event = '$pageview' // OPTIMIZED: Use subqueries for complex analysis const retention = await queryPostHog(` SELECT dateTrunc('week', first_seen) AS cohort_week, dateTrunc('week', timestamp) AS activity_week, uniq(distinct_id) AS users FROM events INNER JOIN ( SELECT distinct_id, min(timestamp) AS first_seen FROM events WHERE event = 'user_signed_up' AND timestamp > now() - interval 90 day GROUP BY distinct_id ) AS cohorts ON events.distinct_id = cohorts.distinct_id WHERE timestamp > now() - interval 90 day GROUP BY cohort_week, activity_week ORDER BY cohort_week, activity_week `);
Step 5: Session Recording Performance
// Limit session recording to reduce data volume and cost posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, { api_host: 'https://us.i.posthog.com', session_recording: { // Only record 10% of sessions sampleRate: 0.1, // Minimum session duration to record (skip quick bounces) minimumDurationMilliseconds: 5000, // Mask all text inputs by default maskAllInputs: true, // Mask specific CSS selectors maskTextSelector: '.sensitive-data', }, });
Performance Benchmarks
| Operation | Without Optimization | With Optimization |
|---|---|---|
| Feature flag evaluation | 50-200ms (network) | <1ms (local eval) |
| Event capture | Individual sends | Batched (20 events/req) |
| HogQL query (7d) | 2-5s | <1s (with filters) |
| HogQL query (no filter) | 30-60s (timeout risk) | N/A (always filter) |
Error Handling
| Issue | Cause | Solution |
|---|---|---|
| Events dropped on exit | No shutdown hook | Add to SIGTERM handler |
| Flag evaluation slow | No | Add personal API key for local evaluation |
| High event cost | Capturing everything | Implement sampling |
| HogQL timeout | No date filter | Always include |
| Session recordings large | Recording all sessions | Set to 0.1-0.25 |
Output
- Local feature flag evaluation (<1ms per check)
- Optimized batching configuration
- Event sampling with
before_send - Efficient HogQL query patterns
- Session recording sampling