VoidFlag is a schema-first feature flag and remote configuration management system. Designed to replace stringly-typed and error-prone feature toggles, VoidFlag introduces a custom .vf schema language to rigorously define flags, their data types, and fallback values.
The core problem VoidFlag solves is the lack of type-safety, poor developer experience, and tedious local-testing loops inherent in many feature flag platforms. By pairing a robust schema definition with automatic code generation (SDK clients), local dev-servers, and varying transport strategies, VoidFlag offers a GitOps-friendly, local-first workflow that scales up to a full production system.
VoidFlag is structured as a Turborepo monorepo with multiple distinct packages and applications:
- Backend (
apps/api): A NestJS application handling core business logic, user auth, project/environment management, and flag distribution. - Frontend (
apps/frontend): A Next.js application serving as the dashboard for developers to manage flags and environments. - Developer CLI (
packages/CLI): A toolset for local development (dev server), schema parsing, and client generation. - SDK (
packages/sdk): A strictly-typed TypeScript client consumed by end-user applications. - AST Parser (
packages/parser): The lexer and parser for.vfschema files. - VS Code Extension (
packages/vscode-extension): Syntax highlighting and IDE support for.vffiles.
Data Flow
- Developers define flags in a
schema.vffile. - The CLI generates a strongly-typed TypeScript client representing that specific schema.
- In local development, the CLI spins up a Dev Server, capturing API requests from the SDK and streaming flag updates via Server-Sent Events (SSE).
- In production, the schema is pushed to the central Backend. The SDK connects to the backend, which determines the optimal transport mechanism (Polling vs. SSE) based on connection stability and the user's billing plan.
- Purpose: Centralized source of truth for projects, user environments, and feature flag states.
- Key Responsibilities: Rate-limiting (via NestJS Throttler), authentication (JWT/OAuth2), schema syncing (validating pushed
.vfschemas), and streaming flag states to connected clients. - Notable Implementation: The
/connectendpoint inspects theuserPlan. Users on theFREEtier are restricted to apollingtransport mechanism, while paid tiers receive a persistentssestream.
- Purpose: Developer dashboard.
- Key Responsibilities: Visualizing flag states, managing environments, and modifying properties like rollout percentages.
- Notable Implementation: Built on Next.js 16, utilizing modern React hooks, Zustand for local state management, TailwindCSS v4, and shadcn/ui components.
- Purpose: The bridge between the platform and the developer's local environment.
- Commands:
vf generate: Readsschema.vf, parses the AST, and generates heavily typedvoidflag.client.tscode for the developer's app.vf dev: Starts an Express-based local dev server, injecting itself in place of the production backend. It exposes a minimal local UI to toggle flags during local development.vf apply: Pushes local flag states/schemas up to the backend server.
- Purpose: The runtime library integrated into user applications.
- Modules/Files:
sdk.ts,transport.ts. - Implementation: Utilizes djb2 hashing for stable user rollout allocations. Employs lazy-evaluation via an accessor cache
Object.definePropertyfor optimal read performance.
- Access Patterns: The SDK converts schema definitions into
Accessornodes. This forces O(1) property lookups (e.g.,vf.flags.darkMode.value) instead of method calls (vf.get('darkMode')), which is cleaner and highly cacheable. - Transport Mechanisms:
- SSE (
SSETransport): Primary real-time mechanism usingEventSource. Resolves stream updates incrementally using patches. - Polling (
PollingTransport): Standard HTTP polling mechanism. - Resilience & Backoff: Features a full-jitter exponential backoff algorithm to prevent "thundering herd" scenarios when clients reconnect. If the SSE stream repeatedly fails, the SDK permanently degrades into the Polling transport.
- SSE (
- Typing Strategy: Generics-heavy (
VoidClient<S extends FlagMap>). Extracting types strictly ensures that developers cannot query flags that do not exist or expect an incorrect return type (e.g., expecting anumberfrom abooleanflag).
- Workflows Enabled: Local-first development. The local Dev Server runs completely offline, overriding the SDK's remote endpoints. Developers can test complex feature rollouts on
localhostwithout touching a staging server. - Codegen: Central to the UX, the CLI analyzes standard definitions and continuously overwrites a local stub, ensuring zero-latency typescript autocompletion.
- The
.vfFormat: A declarative DSL (Domain Specific Language) dedicated solely to feature flags.flag darkMode { type bool fallback false } - Parser & Lexer: A hand-written lexer/parser (
packages/parser). It iterates over strings, tokenizes constraints (flag,type,fallback), and generates aSchemaAST. By owning the grammar, VoidFlag avoids JSON limitations (like lack of comments) and YAML complexities.
- VS Code Extension:
voidflag-vscode. Utilizing LSP (Language Server Protocol), it provides real-time.vfsyntax highlighting, JSON schema validation for config files (voidflag.config.json), and custom icons. - Code Generation: Prevents typos natively. Instead of guessing a string key, developers get IDE intellisense.
- SSE vs Polling (Plan-Based Routing): Connection concurrency can be incredibly expensive for backend architectures. By gracefully reverting free-tier users into long-polling architectures, the platform limits expensive active socket limits on the Node.js APIs.
- djb2 Hashing for Rollouts: Allows consistent fractional rollouts (e.g., "50% of users"). User X evaluating the flag will always land in the same bucket locally or globally, completely eliminating server-side evaluation calls out to an external system.
- Schema-First (DSL): Hardcoding a schema text file forces a "source of truth". Flags aren't hidden unreachably in a database; they live beside the code.
- Unmatched Type-Safety: End-to-end TS types branching straight from a DSL.
- Pristine Local Development: The inclusion of an explicit local Dev Server command that mocks the live endpoint eliminates the biggest friction point of feature flags—testing them.
- Architectural Resilience: The fallback mechanism smoothly degrades connections from SSE to polling without interrupting the user application.