FlagForge: feature flags in Rust, with one engine everywhere
A multi-tenant feature-flag service I built from scratch in Rust: targeted rollouts, A/B experiments, an audit trail and a dashboard compiled to WebAssembly. It answers one question, whether this user should get this feature, and it has to answer the same way on every server.

What it does
- Targeting rules with seventeen operators, including regex, semver and set membership.
- Reusable segments, so widening an audience is one edit instead of twelve.
- Percentage rollouts that are deterministic and sticky, with no shared state between servers.
- A/B experiments that end in a verdict: rates, 95% confidence intervals and a significance test against the control.
- An audit trail with a before and after for every change.


Decisions worth explaining
- Evaluation never touches the database. A service may check flags on every request, so each node keeps an immutable snapshot in memory, with lock-free reads.
- PostgreSQL tells the nodes when to reload. Triggers send a notification on every write, with no message broker. Because this lives in the database, a change made by anything, even a migration, refreshes every node.
- An invalid flag can never reach the database. The API and the dashboard run the same validation, so an error appears as you type, with the same message the API would return.
- Two operators cannot silently overwrite each other. A write carries the version it expects, PostgreSQL resolves the race in one statement, and the loser gets a 409 that says what happened.
A decision I had to reverse
The snapshot originally withheld the bucketing salt, and a test asserted it never appeared. That felt prudent and was wrong. Without the salt, an SDK matches rules correctly but assigns rollouts differently from the server, so every answer looks plausible and roughly a quarter of them are wrong, with nothing to indicate it.
The salt now goes to server-scoped keys, and only to them. Client-scoped keys still get decisions only, and tests pin that boundary.
Speed and tests
| Flag shape | Time |
|---|---|
| On, no rules | 40 ns |
| Percentage rollout | 114 ns |
| 5 rules, the last one matches | 298 ns |
| 20 rules, none match (worst case) | 985 ns |
It is a plain benchmark, enough to back the claim and notice a regression, not a statistical study.
There are 245 tests in four layers: 86 for the domain, including property tests with proptest, 79 for HTTP, 65 for integration, each with its own migrated database, and 15 for the SDK. One of them starts a real server and compares 300 users evaluated locally and remotely.