Variable
Variable is a type-safe feature flag system. You define features in .var files, and Variable generates typed code so you never misspell a flag name, pass the wrong type, or forget a default.
1: Feature Checkout = {
1: Variable enabled Boolean = true
2: Variable max_items Number = 50
3: Variable header_text String = "Complete your purchase"
}
Run one command:
Get type-safe TypeScript:
import { VariableClient } from "@variable/runtime";
import { getCheckoutVariables } from "./generated/features.generated";
const client = new VariableClient();
const checkout = getCheckoutVariables(client);
checkout.enabled // boolean
checkout.max_items // number
checkout.header_text // string
No string lookups. No as any. Full autocompletion.
Getting Started
Prerequisites
Build from source
&&
The binary is at target/release/variable.
Quick start
1. Write a .var file
1: Feature Search = {
1: Variable enabled Boolean = false
2: Variable max_results Number = 25
3: Variable placeholder String = "Search..."
}
2. Generate TypeScript
This creates src/generated/search.generated.ts containing:
- A
SearchVariablesinterface with typed fields - A
getSearchVariables()function that returns values with defaults baked in
3. Use in your code
import { VariableClient } from "@variable/runtime";
import { getSearchVariables } from "./generated/search.generated";
const client = new VariableClient();
const search = getSearchVariables(client);
console.log(search.max_results); // 25
The VariableClient accepts a Provider that can supply overrides (from a remote service, local config, etc.). With no provider, you get the defaults from your .var file.
4. Encode binary snapshot bytes
The encode command writes the full snapshot payload to stdout, so it can be piped to files or other tools.
5. Decode binary snapshot bytes for diagnostics
decode emits JSON containing decoded snapshot content and diagnostics (without attempting to reconstruct .var source).
The .var DSL
A .var file contains one or more Feature blocks. Each feature has typed variables with mandatory defaults.
1: Feature Checkout = {
1: Variable enabled Boolean = true
2: Variable max_items Number = 50
3: Variable header_text String = "Complete your purchase"
}
2: Feature Search = {
1: Variable fuzzy_matching Boolean = false
2: Variable max_results Number = 25
3: Variable placeholder String = "Search..."
}
Supported types
| Type | TypeScript | Example |
|---|---|---|
Boolean |
boolean |
true, false |
Number |
number |
42, 3.14 |
String |
string |
"hello", "line\nnewline" |
Rules
- Feature names must be unique across the file
- Feature IDs must be unique across the file
- Variable names must be unique within a feature
- Variable IDs must be unique within a feature
- Feature and variable IDs are required (
u32) - Every variable must have a default value
- String literals support escape sequences:
\n,\t,\\,\"
What gets generated
For a feature named Checkout, Variable generates:
// This file is generated by Variable. Do not edit.
import { VariableClient } from "@variable/runtime";
export interface CheckoutVariables {
enabled: boolean;
max_items: number;
header_text: string;
}
const checkoutDefaults: CheckoutVariables = {
enabled: true,
max_items: 50,
header_text: "Complete your purchase",
};
export function getCheckoutVariables(client: VariableClient): CheckoutVariables {
const overrides = client.getFeatureValues("Checkout");
return {
...checkoutDefaults,
...overrides,
} as CheckoutVariables;
}
Defaults are baked in. Overrides from a provider are spread on top. You always get a complete, typed object.
Runtime
The @variable/runtime package (in runtime/ts/) provides the types that generated code imports:
import { VariableClient, NoopProvider } from "@variable/runtime";
// Default: no overrides, just use .var defaults
const client = new VariableClient();
// Or bring your own provider
const client = new VariableClient(myProvider);
A Provider implements one method:
interface Provider {
getManifest(): Record<string, Record<string, unknown>>;
}
The NoopProvider returns an empty manifest, so all values fall back to the defaults in generated code.
Examples
Working examples are in examples/. Each is a self-contained project you can run.
Basic
Generates code from a .var file and uses it at runtime:
=== Checkout Feature ===
enabled: true
max_items: 50
header_text: Complete your purchase
=== Search Feature ===
enabled: false
max_results: 10
placeholder: Search...
All assertions passed!
Project Structure
variable-core/ Lexer, parser, AST, validation
variable-codegen/ TypeScript code generation
variable-wire/ Binary wire-format encode/decode tooling
variable-cli/ CLI binary (the `variable` command)
runtime/ts/ TypeScript runtime (@variable/runtime)
examples/ts/ TypeScript usage examples
fixtures/ Sample .var files for testing
Development
# Run all tests
# Build the CLI
# Update codegen snapshots after changing output format
License
MIT