Skip to content

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

Open
linear-code[bot] wants to merge 2 commits into
mainfrom
linear/sdk-3-enable-flag-dependency-in-the-go-server-side-sdk-3091
Open

linear-code[bot] wants to merge 2 commits into
mainfrom
linear/sdk-3-enable-flag-dependency-in-the-go-server-side-sdk-3091

Conversation

@linear-code

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

Copy link
Copy Markdown

Local evaluation in the Go SDK ignored Flag Dependency. A segment condition on another flag ($.flags.<feature>.enabled, .value or .variant) never resolved, so a feature that should switch on when its prerequisite is enabled kept its default. This change ports flagsmith-engine#343, so local evaluation now matches the engine and Edge API.

Changes

  • Identity flags that depend on other flags, directly or through a chain, now get their segment overrides when evaluated locally.
  • Circular dependencies report ERROR; code=CIRCULAR_DEPENDENCY and serve the environment default. Segments that read such a flag don't match.
  • Only properties starting with $. count as flag paths. $['flags']… is still read as a trait name, as in the engine.
  • CI now runs engine-test-data v3.11.0, the release that adds the flag dependency cases. All 219 cases pass.
  • Evaluations without flag dependencies cost the same as before or less. Evaluations with them cost a few percent more (benchmarks below).

The shared data lists matched segments in the order the context declares them. Go segment maps have no declaration order, and the engine always sorts segments by key, so the test harness now compares matched segments regardless of order.

I didn't go up to v3.12.x (#227) here. v3.12.0 also expects the new STATIC reason (flagsmith-engine#347), which would change reasons users already see on environment flags. That deserves its own PR.

When merged, release-please will propose a minor release (5.4.0). A maintainer ships it.

Performance

BenchmarkGetEvaluationResult calls only the public flagengine.GetEvaluationResult, so the same file runs on the baseline, 626676c (main before this PR). Workloads:

  • the five contexts that flagsmith-engine benchmarks
  • every engine-test-data case, with and without flag dependencies
  • a synthetic identity context with 200 features and 100 segments; the dependency variant chains 20 segments on $.flags

Both sides read the same v3.11.0 test data. Runs were interleaved, 10 per side, on Go 1.25.0, linux/amd64, a single-vCPU Xeon 8375C.

Workload time B/op allocs/op
upstream benchmark contexts ~ (p=0.12) −7.9% +2.1%
test data without flag dependencies ~ (p=0.63) −7.9% +1.7%
test data with flag dependencies +2.6% (p=0.035) −8.8% +70%
synthetic without flag dependencies −13.2% −25.7% −1.0%
synthetic with flag dependencies +5.7% (p=0.001) −13.1% +15.6%

The first version of this PR was 19–28% slower even without flag dependencies. That's fixed in 5649d9a: the resolver now sets up its state only when a condition reads $.flags, segment keys are sorted once per evaluation, flag fields are read directly instead of through a JSONPath query, and dependency flags are not evaluated twice. The extra allocations are the resolver itself (one per evaluation) plus its per-evaluation index once dependencies are read.

Limitations:

  • The "with flag dependencies" rows aren't like-for-like. The baseline never resolves those flags, so it does less work and returns wrong flags.
  • Results come from one noisy shared vCPU, so read differences of a few percent with that in mind.
  • These benchmarks measure the engine only. SDK calls such as GetIdentityFlags wrap the same function, but I didn't benchmark them separately.
git worktree add /tmp/baseline 626676c
rmdir /tmp/baseline/flagengine/engine-test-data
ln -s "$PWD/flagengine/engine-test-data" /tmp/baseline/flagengine/engine-test-data
cp flagengine/engine_benchmark_test.go /tmp/baseline/flagengine/
for i in $(seq 10); do
  (cd /tmp/baseline && go test -run '^$' -bench . ./flagengine/ | grep Benchmark >> /tmp/baseline.txt)
  go test -run '^$' -bench . ./flagengine/ | grep Benchmark >> /tmp/branch.txt
done
go run golang.org/x/perf/cmd/benchstat@latest /tmp/baseline.txt /tmp/branch.txt

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

Review effort: 3/5

…ocal evaluation

Segment conditions on `$.flags.<feature>.enabled`, `.value` or `.variant`
now resolve the referenced flag, following transitive dependencies. Flags
caught in a dependency cycle serve their environment default with reason
`ERROR; code=CIRCULAR_DEPENDENCY`, and segments reading them do not match.
Ports Flagsmith/flagsmith-engine#343.

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

Generated with [Linear](https://linear.app/flagsmith/agent-session/23ffa3ca-707e-4c94-9b8e-d730e6e58c4c)

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:16
@linear-code
linear-code Bot requested review from emyller and removed request for a team October 21, 2026 17:16
@coderabbitai

coderabbitai Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Important

Review skipped

Bot user detected.

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

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 151b65bf-68a8-44b5-96cb-eddfbcbf805d

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.

…aseline cost

The dependency resolver now allocates its state only once a condition
reads `$.flags`, segment keys are sorted once per evaluation, flag fields
are read directly instead of through a JSONPath query, and flags resolved
as dependencies are not evaluated twice.

Generated with [Linear](https://linear.app/flagsmith/agent-session/23ffa3ca-707e-4c94-9b8e-d730e6e58c4c)

Co-authored-by: linear-code[bot] <222613912+linear-code[bot]@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@emyller
emyller force-pushed the linear/sdk-3-enable-flag-dependency-in-the-go-server-side-sdk-3091 branch from 5649d9a to 0cd5780 Compare October 27, 2026 02:23
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