posthog-performance-tuning
Techniques for tuning PostHog performance, focusing on latency reduction and efficient query patterns.
Install
mkdir -p .claude/skills/posthog-performance-tuning && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/7844" && unzip -o skill.zip -d .claude/skills/posthog-performance-tuning && rm skill.zipInstalls to .claude/skills/posthog-performance-tuning
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Optimize PostHog performance: local flag evaluation, client batchingKey capabilities
- →Enable local feature flag evaluation
- →Configure event batching settings
- →Implement event sampling via before_send
- →Execute optimized HogQL queries
- →Limit session recording volume
How it works
Reduces network overhead by caching feature flag definitions locally and batching outgoing event requests. It also optimizes data ingestion through sampling and efficient SQL-based query filtering.
Inputs & outputs
When to use posthog-performance-tuning
- →Enable local feature flag evaluation
- →Configure batch request settings
- →Optimize HogQL query performance
- →Sample events to manage high-throughput data
About this skill
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
posthog-nodeand/orposthog-jsinstalled- Personal API key (
phx_...) for local flag evaluation - 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 posthog.shutdown() to SIGTERM handler |
| Flag evaluation slow | No personalApiKey | Add personal API key for local evaluation |
| High event cost | Capturing everything | Implement before_send sampling |
| HogQL timeout | No date filter | Always include timestamp > now() - interval N day |
| Session recordings large | Recording all sessions | Set sampleRate 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
Resources
When not to use it
- →Avoid HogQL queries without time filters
- →Do not skip shutdown hooks in serverless environments
Prerequisites
Limitations
- →Local flag evaluation requires a personal API key
- →HogQL queries without date filters risk timeouts
- →Session recording sampling reduces data granularity
How it compares
Unlike default configurations that send every event and perform network-based flag checks, this approach prioritizes local evaluation and controlled ingestion to minimize latency and cost.
Compared to similar skills
posthog-performance-tuning side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| posthog-performance-tuning (this skill) | 1 | 27d | Review | Intermediate |
| mcp-builder | 136 | 3mo | Review | Advanced |
| chrome-devtools | 41 | 7mo | Review | Intermediate |
| bullmq-specialist | 25 | 6mo | No flags | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by jeremylongshore
View all by jeremylongshore →You might also like
mcp-builder
anthropics
Guide for creating high-quality MCP (Model Context Protocol) servers that enable LLMs to interact with external services through well-designed tools. Use when building MCP servers to integrate external APIs or services, whether in Python (FastMCP) or Node/TypeScript (MCP SDK).
chrome-devtools
mrgoonie
Browser automation, debugging, and performance analysis using Puppeteer CLI scripts. Use for automating browsers, taking screenshots, analyzing performance, monitoring network traffic, web scraping, form automation, and JavaScript debugging.
bullmq-specialist
davila7
BullMQ expert for Redis-backed job queues, background processing, and reliable async execution in Node.js/TypeScript applications. Use when: bullmq, bull queue, redis queue, background job, job queue.
swarm-advanced
ruvnet
Advanced swarm orchestration patterns for research, development, testing, and complex distributed workflows
agentdb-memory-patterns
ruvnet
Implement persistent memory patterns for AI agents using AgentDB. Includes session memory, long-term storage, pattern learning, and context management. Use when building stateful agents, chat systems, or intelligent assistants.
agentdb-advanced-features
ruvnet
Master advanced AgentDB features including QUIC synchronization, multi-database management, custom distance metrics, hybrid search, and distributed systems integration. Use when building distributed AI systems, multi-agent coordination, or advanced vector search applications.