Skip to content

feat(Flag Dependency): Evaluate flags that depend on other flags in local evaluation - #205

Open
linear-code[bot] wants to merge 2 commits into
mainfrom
linear/sdk-7-enable-flag-dependency-in-the-net-server-side-sdk-818e
Open

linear-code[bot] wants to merge 2 commits into
mainfrom
linear/sdk-7-enable-flag-dependency-in-the-net-server-side-sdk-818e

Conversation

@linear-code

@linear-code linear-code Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Segments can now be conditioned on another flag's evaluated result, e.g. "enable checkout for whoever has beta_access on". Edge API already does this through flagsmith-engine#343. Until now the .NET engine ignored these conditions, so local evaluation served the wrong flags for any project that uses them. This PR ports the same behaviour to the .NET engine.

Changes

  • Segment conditions on $.flags.<feature>.enabled, .value or .variant read that flag's evaluated result, including chains of dependencies. Flags are resolved only when a condition reads them.
  • Flags caught in a dependency cycle never take a dependent override and are reported with the reason ERROR; code=CIRCULAR_DEPENDENCY.
  • Flags supplied in the incoming context are ignored. Only properties starting with $. are read as paths; $['flags']… is treated as a trait name, same as the reference engine.
  • Flag results now include the multivariate variant (the variant key, control, or null). Local evaluation reads the key from the environment document, so .variant dependencies work there too.
  • engine-test-data is bumped v3.10.0 → v3.11.0, the release that adds the flag-dependency cases. All 251 cases pass; before this change 13 failed.
  • Evaluation performance is on par with the pre-feature engine for existing workloads, and dependent flags cost no more than repeating the trait rules they depend on (see Performance below).
  • Not included: v3.12.x adds the separate STATIC reason change (flagsmith-engine#347). Renovate deps: update dependency flagsmith.enginetest/enginetestdata to v3.12.1 #198 will need that change ported before it can pass.

Override precedence keeps this SDK's existing tie-break (on equal priority, the segment seen last wins). The reference engine picks the first seen. That difference is older than this PR and left unchanged here.

Performance

A new Flagsmith.Engine.Benchmarks project (BenchmarkDotNet 0.15.8) compares this branch against baseline e2c5320, the commit before flag dependency, using the same workloads and the same engine-test-data v3.11.0 inputs. The synthetic environment has 200 features (20 multivariate) and 50 segments with nested trait rules. The dependency workload adds 10 prerequisite flags and 10 segments reading $.flags.prerequisite_N.enabled; its control repeats the prerequisite trait rules instead and produces identical flags. Commands are in the project README:

cd Flagsmith.Engine.Benchmarks && dotnet run -c Release -- --filter '*'   # here and in a worktree at e2c5320

Median time per evaluation, range across 3 baseline runs and 2 branch runs, interleaved. Allocation per evaluation is deterministic.

Workload Baseline e2c5320 This PR e7ea5c8
Environment flags (no identity) 8.4–9.0 µs, 39.1 KB 9.1–9.2 µs, 41.5 KB
Identity, trait segments 145.7–150.5 µs, 170.3 KB 148.1–148.2 µs, 172.8 KB
Identity, + 10 JSONPath segments 6.2–6.3 ms, 11,873 KB 6.5 / 24.0 ms†, 11,876 KB
engine-test-data, 200 non-dependency cases 7.0–21.9 ms†, 8,931–8,991 KB 6.5 ms, 8,963 KB
Identity, segments repeating trait rules 252–258 µs, 276 KB 256–260 µs, 279 KB
Identity, segments depending on flags 7.0–25.4 ms, 11,716 KB (wrong flags) 246–250 µs, 308 KB
engine-test-data, 19 flag_dependency cases 223–239 µs, 464 KB (wrong flags) 63 µs, 158 KB
  • Existing workloads are within run-to-run noise. The exception is the cheapest one, environment flags, which is about 0.4 µs (~5%) slower and allocates 2.4 KB more. This is consistent with the extra variant field on each of the 200 flag results, though I haven't measured that separately.
  • Reading a flag is faster than repeating the trait rules, but allocates about 10% more, because each read serialises the prerequisite flag for the JSONPath lookup. The first version of this branch took 299 µs / 350 KB because it evaluated prerequisite segments twice. The second commit fixes that by reusing segment matches, and also skips the resolver's allocations when no condition reads a flag.
  • Baseline numbers for dependency workloads only show cost, since it serialised the whole context per condition and returned wrong flags.
  • Limitations: measured on .NET 10.0.13 only, on a shared 1-vCPU Linux VM where BenchmarkDotNet couldn't raise process priority. †Allocation-heavy workloads swung 3–4× between runs on both revisions, so their medians are unreliable and allocations are the better comparison. Not measured: full client GetIdentityFlags, .NET 6–9, or other hardware.

Contributes to https://linear.app/flagsmith/issue/SDK-7/enable-flag-dependency-in-the-net-server-side-sdk

Review effort: 4/5

…ocal evaluation

Port flagsmith-engine#343 to the .NET engine. Segment conditions reading
`$.flags.<feature>.enabled`, `.value` or `.variant` now resolve that flag's
evaluated result lazily, including transitive chains. Flags in a dependency
cycle are reported with the `ERROR; code=CIRCULAR_DEPENDENCY` reason.

Flag results now carry the selected multivariate `variant`, mapped from the
option key in the environment document, so dependencies on variants work.

Bump engine-test-data to v3.11.0, which adds the flag dependency cases.

Generated with [Linear](https://linear.app/flagsmith/agent-session/be5fe950-2490-482f-91fa-7a20fa7fdec3)

Co-authored-by: linear-code[bot] <222613912+linear-code[bot]@users.noreply.github.com>
@linear-code
linear-code Bot requested a review from a team as a code owner October 21, 2026 17:22
@linear-code
linear-code Bot requested review from emyller and removed request for a team October 21, 2026 17:22
…hmarks

Segment matches resolved for flag dependencies are now reused by the main
segment loop instead of being evaluated again, and the dependency resolver
only allocates its state once a condition reads a flag.

Adds a BenchmarkDotNet project comparing engine revisions over synthetic
environments and the engine-test-data cases.

Generated with [Linear](https://linear.app/flagsmith/agent-session/be5fe950-2490-482f-91fa-7a20fa7fdec3)

Co-authored-by: linear-code[bot] <222613912+linear-code[bot]@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Oct 21, 2026

Copy link
Copy Markdown

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: b7fa49b2-3009-4897-8f46-a4958bc504d9

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant