Skip to main content
The @talosjs/feature-flag component is a small, typed contract for gating functionality behind toggles. Each flag is an injectable class implementing the IFeatureFlag interface (getKey, getDescription, and isEnabled), registered with a decorator and resolved from the container. The enablement check can be synchronous or asynchronous, so a flag can return a constant, read an environment variable, or query a remote service.

What it offers

Every flag implements the same IFeatureFlag interface, so call sites evaluate them identically no matter how each flag reaches its decision. isEnabled() may return a boolean or a Promise<boolean>, which lets you back a flag with a constant, an env var, config, or a remote check without changing the callers. Each flag also carries a stable getKey() and a human-readable getDescription(), so the set of flags stays discoverable and auditable. Registration is a decorator, the DI container resolves the flag at any scope, and the types (IFeatureFlag and FeatureFlagClassType) are there throughout.

How it works

You implement IFeatureFlag as a class, register it with the featureFlag decorator, and resolve it from the container. Callers invoke isEnabled() and branch on the result. The interface is the whole contract:
Because isEnabled() can be async, the same flag shape covers constant toggles, environment-driven rollouts, and remote configuration. Callers always await the result and never need to know the source.

Decorator and usage

@decorator.featureFlag(scope?)

Registers a feature-flag class with the DI container. It accepts an optional EContainerScope (defaults to EContainerScope.Singleton). Implement IFeatureFlag and register it:
isEnabled() can resolve a value from a remote service, database, or environment:
Resolve the flag from the container and branch on the result:
Pass a scope when a flag should not be a singleton, for example to re-evaluate it per request:

Using flags well

Keep keys stable and unique. Use kebab-case like dark-mode and beta-checkout, and never reuse or rename a key once it ships, because logs and config may reference it. Write a real description, since getDescription() is what makes flags auditable: say what the flag controls and who it targets, not just its name. Treat isEnabled() as potentially async even for constant flags and always await it, so a flag can later move to a remote check without touching callers. Keep the decision in one place. All gating logic belongs inside isEnabled(), and callers should only branch on the boolean rather than re-deriving the condition. Match the scope to the source: the default singleton suits constant or env-driven flags, while Transient or Request fits a flag that must be re-evaluated per request. When a flag reads config or calls a service, inject those dependencies through the constructor instead of reaching for globals.

CLI command

Scaffold a feature flag class and its test file with the generator. It writes the class under modules/<module>/src/flags/<Name>FeatureFlag.ts and installs @talosjs/feature-flag if it is missing.
The generated class is a ready-to-complete stub with the key pre-filled in kebab-case from the name:
See flag:create for the full command reference.

Use with Claude and Codex

The generator ships a matching flag:create skill. It runs the scaffold and then guides your AI agent through completing the flag: setting a stable key, writing the description, and implementing isEnabled() with the real gating logic. Initialize the skills once for your agent:
Then ask Claude in natural language. It maps the request to the generator, runs it, and fills in the implementation:
Prompt
For example, the prompt above maps to flag:create --name=NewCheckout, then implements getKey, getDescription, and isEnabled for the new checkout flow.