← Blog

The PostHog alternative for teams that want flags in git

The PostHog alternative for teams that want flags in git

dif is the PostHog alternative for teams that want a feature flag to be a file in their repo, not a row in PostHog’s dashboard. PostHog bundles feature flags into a product analytics platform: flags live alongside session replay, surveys, and event analytics, configured through the PostHog web app. dif does one thing. A flag is a Markdown file next to the code it gates, reviewed in a pull request, with no analytics subscription attached.

Both tools are open source, and PostHog’s free tier for flags is generous. The difference is the model, not the price. PostHog stores the flag definition on its servers and your SDK fetches it; dif stores the flag in your repo and your SDK never calls out at all. This post is about where that split matters, where PostHog’s breadth still wins, and what moving a flag into a .md file actually looks like.

Key Takeaways

  • dif keeps flags as Markdown files in your repo. PostHog stores flag definitions on its servers and configures them through the PostHog web app.
  • PostHog’s free tier includes 1 million feature flag requests a month, then $0.0001 per request. dif’s SDK makes no request at all; it hashes the user ID locally.
  • Self-hosting PostHog’s open-source core means running a 7+ service stack (Django, Postgres, ClickHouse, Redis, Kafka, MinIO), with 4 vCPU and 16 GB RAM as the documented baseline. dif’s core has no server.
  • PostHog ships SDKs for a dozen-plus languages, including Go, Java, PHP, Ruby, and Rust. dif’s SDK is JavaScript and TypeScript, with React and Svelte adapters.
  • dif emits a context.json a coding agent reads directly from the repo. PostHog’s MCP server gives an agent the same kind of access, but over a live API call to PostHog’s platform, not a file already in your checkout.

Why teams look for a PostHog alternative

PostHog is not a weak tool. Teams go looking for an alternative for one of three reasons.

The bundle. Feature flags in PostHog are one product among analytics, session replay, surveys, and error tracking. A team that wants flags and nothing else is paying attention to (and paying for, past the free tier) a platform built for a bigger job.

The dashboard is the source of truth. A flag’s release conditions, rollout percentage, and targeting rules live on PostHog’s servers, set through the web app. The SDK caches a copy for local evaluation, but the authoritative definition is not a file you can diff, review, or git blame.

The self-hosting footprint. PostHog’s open-source core is real and self-hostable, but running it means operating a full analytics stack: Django, Postgres, ClickHouse, Redis, Kafka, and MinIO, at a documented baseline of 4 vCPU and 16 GB RAM. That is a lot of infrastructure to carry for the subset of teams that only wanted the flag.

dif as the alternative in one paragraph

dif takes the flag off PostHog’s servers and makes it a file. dif init writes a dif/ directory next to your source. Each flag is Markdown: YAML frontmatter up top (id, owner, variants, weights, audience), prose below it (the brief, the decision, the learning). dif validate runs in CI. dif build compiles the active files into a typed client your app imports, and a context.json your coding agent can read. The flag lives with the code it gates, gets reviewed in the same pull request, and its history is the git history. See feature flags in your repo for the full model.

The model, side by side

PostHog’s feature flags are a hosted product. You set the release conditions in the web app, the SDK fetches the definition (and, with local evaluation turned on, caches it and re-polls on an interval), and evaluation happens against that cached copy plus whatever person or group properties you pass in. Changing a flag is an edit in the dashboard that takes effect the next time a client polls.

dif’s feature flags are a file format. The flag is a Markdown file in dif/experiments/active/. Changing a rule is editing that file and merging a pull request. The SDK computes the variant locally by hashing the user ID against the flag’s weights, so there is no fetch and nothing to poll.

PostHog’s model is built for an operator changing a rollout right now from a dashboard, with an analytics pipeline already attached to tell you what happened. dif’s model is built for a codebase where the flag is a diff, and the reviewer wants to see the rule and the call site in one pull request.

Feature comparison

DimensiondifPostHog
Where flags liveMarkdown files in your repoPostHog's servers, set in the web app
Server to runNoneDjango + Postgres + ClickHouse + Redis + Kafka + MinIO (self-hosted) or PostHog Cloud
LicenseOpen source (MIT)Open source core, with a hosted Cloud product
ScopeFlags and experiments onlyFlags bundled with analytics, session replay, surveys, error tracking
Flag SDK breadthJavaScript and TypeScript, with React and Svelte adapters12+ languages including Go, Java, PHP, Ruby, Rust, plus mobile and framework SDKs
Free tier for flagsUnlimited; local evaluation makes no request1,000,000 feature flag requests/month, then $0.0001 per request
Change a flagEdit the file, open a PR, merge, deployChange release conditions in the web app; SDK picks it up on next poll
Agent-readable stateYes (context.json, a file in the repo)Yes, via PostHog's MCP server, over a live API call
Audit trailGit history on the flag filePostHog's activity log, inside the product

For a row-by-row table with a dated source for each PostHog claim, see dif vs PostHog.

Where PostHog wins

PostHog earns three rows outright.

Language and framework breadth. If your backend runs Go, Java, PHP, Ruby, or Rust as well as JavaScript, PostHog already ships an SDK for each. dif’s SDK is JavaScript and TypeScript, with React and Svelte adapters. dif does not close that gap.

Flags with analytics already attached. If you want event data, session replays, and a feature flag’s rollout in one product, PostHog gives you that in a single login. dif’s SDK will fire exposure and tracking events, but you either wire them to your own analytics or opt into dif Cloud; there is no bundled product analytics suite.

A dashboard for a non-engineering flag owner. Changing a release condition or watching a rollout land is a web-app action in PostHog, no pull request required. dif has no dashboard; every rule change goes through code review.

Where dif wins

The flag is the file. dif/experiments/active/new-checkout.md is the whole thing. Opening it answers “why does this exist” without logging into a dashboard.

---
id: new-checkout
status: active
owner: sam@acme.com
surface: checkout
hypothesis: >
  Inlining the address form lifts completed checkouts on mobile and tablet.
audience:
  include:
    - device_type: [mobile, tablet]
variants:
  - id: "off"
    weight: 90
  - id: "on"
    weight: 10
metrics:
  primary: completed_checkout
  guardrails:
    - refund_rate
created: 2026-10-02
---

## Brief

Ramping to 25% once the guardrails hold for a week. Owner is Sam.

No analytics stack to run for the core workflow. PostHog’s self-hosted core needs Postgres, ClickHouse, Redis, Kafka, and MinIO behind it, at a documented 4 vCPU and 16 GB RAM baseline. dif’s whole runtime for flags is the CLI and the 5 kB SDK; nothing to patch or back up.

No request to make or meter. dif’s SDK hashes the user ID against the flag’s weights locally, so there is no request to count and no bill that moves with traffic. PostHog’s free tier of 1 million flag requests a month is generous, but it is still a counter, and it resets to $0.0001 per request past that.

Pull-request review is the approval flow. dif validate runs in CI, so a broken flag fails the pull request the same way a broken build does.

Agent-readable state with no API call. dif build writes every active flag and each surface’s latest learning to context.json, a file already in the checkout a coding agent reads on session start. PostHog’s MCP server gets an agent to the same information, but through a live call to PostHog’s API rather than a file that was already there. See context.json explained for how an agent uses it.

Who should switch, and who should stay on PostHog

Stay on PostHog if any of these are true:

  • You want flags, product analytics, session replay, and surveys in one login, and you are fine paying past the free tier of 1 million flag requests a month as traffic grows.
  • Your backend is a mix of Go, Java, PHP, Ruby, or Rust and you need a supported SDK for each today. dif’s SDK is JavaScript and TypeScript with React and Svelte adapters.
  • A non-engineering flag owner is the primary user, and a dashboard they can change without a pull request is the right workflow for your team.

Switch to dif if any of these are true:

  • The flag is a code-review artifact. A rule change should get a PR review next to the call site it gates, not a dashboard edit that takes effect on the next poll.
  • You do not want an analytics platform’s infrastructure (or its usage-based meter) attached to a flag. dif’s whole runtime is the CLI and a 5 kB SDK.
  • A coding agent works in your repo. Claude Code, Cursor, or any agent reads context.json directly, with no API call and no PostHog account required. See the agent skills docs for authoring flags with an agent in the loop.

What migration looks like

Migration is per-flag. The format is small enough that porting by hand is usually faster than scripting it.

Install the CLI and scaffold the workspace:

npm install -g @dif.sh/cli
dif init

Pick one active PostHog flag. Recreate it as a Markdown file in dif/experiments/active/. Map the fields:

  • The PostHog flag key becomes dif’s id
  • Release conditions and rollout percentage become audience and variants with weights
  • Person and group property filters become dif’s audience.include and audience.exclude blocks
  • Multivariate flag payloads become named variants with weights

A PostHog call in TypeScript:

import posthog from "posthog-js";

if (posthog.isFeatureEnabled("new-checkout")) {
  return <NewCheckout />;
}
return <LegacyCheckout />;

The dif equivalent:

import "./dif/generated/client";
import { dif } from "@dif.sh/sdk";

const checkout = dif("new-checkout", {
  off: () => <LegacyCheckout />,
  on: () => <NewCheckout />,
});
return checkout();

Run dif validate in CI. If the port is broken, the same pass that catches a bad flag file catches it:

$ dif validate
error E005 dif/experiments/active/new-checkout.md:1:1: variant weights sum to 95, expected 100
  help: Distribute the variants so the weights total 100.

✗ 1 error(s), 0 warning(s)

Fix it, merge the pull request, and port one flag a week. Keep the PostHog call site until the dif one is confirmed in production, then delete it in the same commit. The feature flags in git post covers the day-to-day workflow once the flags live in the repo.

FAQ

Is dif a PostHog alternative? For feature flags, yes. Both are open source. The difference is where the flag lives: dif stores each flag as a Markdown file in your repo, and PostHog stores the flag definition on its servers, configured through the PostHog web app. dif does not replace PostHog’s analytics, session replay, or surveys products.

Is PostHog good for feature flags? Yes, especially if you already want its analytics suite. PostHog’s flags are free up to 1 million requests a month, and the SDK breadth is wide. The tradeoff is that the flag’s source of truth is PostHog’s dashboard, not a file you review in a pull request.

Can I self-host PostHog just for feature flags? You can, but you take on PostHog’s full open-source stack to do it: Django, Postgres, ClickHouse, Redis, Kafka, and MinIO, with 4 vCPU and 16 GB RAM as the documented baseline. dif has no server for the core flag workflow; the CLI and a 5 kB SDK are the whole runtime.

Does dif have a dashboard like PostHog? No. The flag is a file in your repo, and changing it is a pull request. ls dif/experiments/active/ is the state of everything in flight.

How does dif compare to PostHog for AI coding agents? Both give an agent access to flag state. dif’s dif build writes a context.json file already in the repo, read on session start with no API call. PostHog’s MCP server gives an agent the same kind of access over a live call to PostHog’s API.

What’s the tradeoff with dif? Rolling back a flag is a merge and a deploy, not a dashboard toggle that takes effect on the next poll. If your build takes ten minutes, your worst-case kill is ten minutes. For a payment path that needs a sub-second kill switch, keep a dashboard-backed service for that one flag.

Try dif as your PostHog alternative

The right PostHog alternative depends on what you actually wanted from PostHog. If it was analytics, replay, and surveys with flags along for the ride, PostHog is already built for that. If it was just the flag, and you would rather review a rule change in a pull request than click it into a dashboard, dif is the shorter path.

Install the CLI and scaffold a project. No account, no API key, no dashboard.

npm install -g @dif.sh/cli
dif init

dif init writes the dif/ directory and the managed agent guidance blocks. From there, dif new drafts a flag, dif validate runs in CI, and dif build compiles the active files. The docs cover the CLI and SDK, and the open-source LaunchDarkly alternative and best feature flag tools 2026 posts cover the wider comparison landscape.