Skip to content
cccskills
FreePublished on GitHub

feature-flags

Add, read, or remove a feature flag in the web app (`defineFlag`, PostHog, `ENABLE_*` env flags, `useFeatureFlag`, `isFeatureEnabled`, `ShowFeatureFlag`) or the Rust backend (`crates/feature_flags`, config switches). Use when gating, rolling out, or killing a feature, or when asked to "flag it".

The English summary is being prepared. Showing the original description.

See how to install

Files included (1)

  • SKILL.md5.3 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

Feature flags

Web registry: apps/web/src/lib/core/constant/featureFlags.ts. Backend: see section 5. A flag is never an authorization check.

1. Remote or env-only?

RemoteEnv-only
Source of truthPostHog in deployed envsdefault, optionally overridden by env
Discriminatorkey presentno key
envoptional local overrideoptional override of default
defaultomit / undefined to fall through to PostHogrequired
ReadersuseFeatureFlag, ShowFeatureFlag, isFeatureEnabledisFeatureEnabled only

Most product rollouts are remote. Use env-only when there is no PostHog flag and never will be (compile-time / local kill switch).

Ask before assuming. If the user said "flag it" and did not mention PostHog, prefer remote and confirm. The answer decides where the flag lives, not who sees it: either way a new flag is usually on in dev (see default below).

2. Define it

Add one defineFlag next to its neighbors. Do not add ENABLE_*_FLAG / _OVERRIDE / ENABLE_*() triples or a raw PostHog string at the call site.

  • key (remote): kebab-case, named after the question (enable-foo, disable-browser-turso-cache). Export the object in matching camelCase (enableFoo).
  • env: the name after VITE_, e.g. 'ENABLE_FOO' → VITE_ENABLE_FOO.
  • default: used when env is unset. Turn a new flag on in dev: DEV_MODE_ENV on an env-only flag; on a remote flag onInDev (DEV_MODE_ENV || undefined), not the boolean itself. If it must stay off in dev, say why in a comment above it. On a remote flag, false is a real override and skips PostHog. On-in-local only is LOCAL_ONLY || undefined.
  • Never invert on the flag object. If the product question is "disable X", invert at the call site (!isFeatureEnabled(disableX)).
  • Env-only exports that are used as if (ENABLE_FOO) must be the boolean: defineFlag({ env, default }).enabled. The object is always truthy.
export const enableFoo = defineFlag({
  key: 'enable-foo',
  env: 'ENABLE_FOO',
  default: DEV_MODE_ENV || undefined,
});

export const ENABLE_BAR = defineFlag({
  env: 'ENABLE_BAR',
  default: DEV_MODE_ENV,
}).enabled;

The PostHog MCP (in the repo's MCP config) can look up existing flags and their rollout. Don't create or change a PostHog flag without asking which project and rollout the user wants, and never change a production rollout without explicit permission.

3. Read it

const foo = useFeatureFlag(enableFoo); // component: foo().enabled
<ShowFeatureFlag flag={enableFoo}>{...}</ShowFeatureFlag>
isFeatureEnabled(enableFoo)            // queries, helpers, env-only

A thin useXFlag wrapper around useFeatureFlag(flag) is fine when several call sites share the same read. Compose flags in the wrapper (calendar search requires calendar UI), not on the flag object.

useFeatureFlag / ShowFeatureFlag take a RemoteFlag. Passing an env-only flag is a type error.

4. Get the rollout right

These are the mistakes past flagged launches had to fix:

  • Loading is not off. Until PostHog answers, isFeatureEnabled returns false and useFeatureFlag returns { loading: true }. A cold deep link must wait for loading to clear rather than fall back to the off path (see block-database's load), or the page flashes or degrades.
  • Read reactively (FE-52): call isFeatureEnabled / useFeatureFlag inside a reactive scope, never snapshot it at mount or module load.
  • Gate every entry point: routes, deep links, previews, copy-link, notifications, command menu, mobile surfaces, and agent/MCP tools that expose the feature, not just the main button.
  • Off keeps today's behavior. A flagged change must leave the off path exactly as it was.
  • Keep the kill switch. A remote flag that defaults on (default: true) can no longer be turned off from PostHog; use undefined so PostHog stays in control.

5. Backend

  • Per-user remote flags use crates/feature_flags (read its README.md): construct PostHogFeatureFlags once at the composition root, load one FlagSnapshot per authenticated user id, and read it synchronously. A failed load returns Err; the caller decides the fallback, explicitly. Wire the adapter and its config at the consuming service composition root.
  • Deployment switches (on for an environment, not per user) are MacroConfig bools with a default, configured in Doppler (see the config rule), not PostHog.
  • Keep the backend and web flag keys identical when both gate one feature.

6. Remove it

When a flag is fully rolled out, delete the defineFlag, every reader, and the off path in the same change, and say which PostHog flag can be archived.

Don't

  • Migrate existing FLAG / OVERRIDE / string useFeatureFlag call sites unless you are already changing that flag or were asked to.
  • Invent a second helper (resolveFeatureFlag for new flags, a global flags.foo map, .use() / .Show() on the object).

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

Wrap a new backend endpoint in the TypeScript SDK (packages/sdk), or record it as skipped. Use when `just coverage` fails, or after adding an endpoint to a Rust service.

The English summary is being prepared. Showing the original description.

macro-inc/macro4.8KUpdated Oct 19, 2026

Add or change an in-app feature tour (a view's guided flyover) in the web app. Use when adding a tour to a view, adding or editing tour steps, pointing a step at a new control, or when a step's target needs something opened first.

The English summary is being prepared. Showing the original description.

macro-inc/macro4.8KUpdated Oct 19, 2026

audit

Free

Post-change checks before handing off. Works out which checks apply from the files changed (directory AGENTS.md Checks sections and matching .agents/rules), runs or verifies each, and reports pass/fail/skipped. Use before saying work is done, before pushing, or when asked to audit or verify a change.

The English summary is being prepared. Showing the original description.

macro-inc/macro4.8KUpdated Oct 19, 2026

Enforce hexagonal architecture in the Rust backend. Use before modifying crates/ or Rust services, especially inbound axum/tool/listener adapters, domain services/ports, outbound adapters, authorization, permissions, database access, or external clients.

The English summary is being prepared. Showing the original description.

macro-inc/macro4.8KUpdated Oct 19, 2026

Use when adding collaborative markdown, notes, or descriptions to a feature with collab surfaces. Covers parent entities, service entry points, authorization, content storage, and lifecycle.

The English summary is being prepared. Showing the original description.

macro-inc/macro4.8KUpdated Oct 19, 2026

Build a new AI tool end-to-end — Rust implementation, toolset wiring, infra, schema generation, and frontend UI.

The English summary is being prepared. Showing the original description.

macro-inc/macro4.8KUpdated Oct 19, 2026

See all skills by macro-inc

Report a problem with this skill