feat(flags): send the split serial ID on exposure events (EX-3422) - #1409
Open
danyal002 wants to merge 1 commit into
Open
feat(flags): send the split serial ID on exposure events (EX-3422)#1409danyal002 wants to merge 1 commit into
danyal002 wants to merge 1 commit into
Conversation
The UFC compiler rewrites a holdout into an ordinary allocation before any SDK reads the flag configuration, so an exposure event records no holdout. The split serial ID is the only link back to it. The native SDKs already carry the value. Android SDK 3.13.1 exposes it on UnparsedFlag and the bridge forwards it, so the online path already works. The offline path did not: the precomputed decoder discarded the serial ID because FlagCacheEntry had no field for it. Propagate the serial ID through the precomputed decoder, gated on type alone. A serial ID that is absent or malformed omits the key and keeps the flag, so a bad value never costs a whole flag or its siblings. Serial IDs are zero-based per org, so 0 is a real value and is pinned by a test. The value crosses the bridge as a string because React Native coerces integers to Double. The Android bridge now omits the key instead of sending null when a flag has no serial ID, so both paths produce one shape. @datadog/flagging-core moves to ~2.1.0, which declares serialId on the precomputed flag type. The previous ~2.0.1 pin could not resolve it.
danyal002
marked this pull request as ready for review
September 9, 2026 18:47
danyal002
requested review from
btthomas and
vjfridge
and removed request for
a team
September 9, 2026 18:47
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
The current serialId validation/stringification can still forward values that won’t safely round-trip to native integer parsing, risking silent loss or mis-encoding of the identifier.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR extends the React Native Flags SDK to propagate the feature-flag split serial ID through the JS flag cache and across the React Native bridge so native exposure tracking can attach it to exposure events.
Changes:
- Adds
serialId?: stringtoFlagCacheEntryand propagates it from precomputed configurations when valid. - Updates Android flag snapshot serialization to omit the
serialIdkey entirely when absent (instead of sendingnull). - Bumps
@datadog/flagging-coreto~2.1.0to consume the precomputed flag type that includesserialId.
File summaries
| File | Description |
|---|---|
| yarn.lock | Updates lockfile for @datadog/flagging-core@~2.1.0 and removes the prior transitive @datadog/js-core entry. |
| packages/core/package.json | Bumps @datadog/flagging-core dependency to ~2.1.0. |
| packages/core/src/flags/internal.ts | Adds optional serialId field to the cached flag entry shape. |
| packages/core/src/flags/configuration/precomputed.ts | Propagates serialId into the decoded offline cache (stringified for bridge safety). |
| packages/core/src/flags/configuration/tests/precomputed.test.ts | Adds extensive tests for serialId acceptance/rejection and key omission behavior. |
| packages/core/src/flags/tests/FlagsClient.test.ts | Verifies serialId is passed to native tracking (offline + online) and omitted when absent. |
| packages/core/android/src/main/kotlin/com/datadog/reactnative/DdFlagsImplementation.kt | Omits serialId key when absent by conditionally adding it to the map. |
Review details
- Files reviewed: 6/7 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
btthomas
approved these changes
Sep 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
The SDK now sends the split serial ID on feature flag exposure events.
The serial ID crosses the bridge as a string, because React Native coerces integers to
Double. Native parses the string back to an integer and puts it on the exposure event.packages/core/src/flags/configuration/precomputed.tsserialIdfrom a precomputed configuration into the flag cache.packages/core/src/flags/internal.tsserialIdfield toFlagCacheEntry.packages/core/android/.../DdFlagsImplementation.ktserialIdkey when a flag has no serial ID.packages/core/package.json,yarn.lock@datadog/flagging-coreto~2.1.0.The decoder gates on type alone. It accepts a finite number. It rejects every other value, including the explicit
nullthat a precomputed assignment sends when a subject has no serial ID. A rejected value omits the key and keeps the flag, so one malformed serial ID never costs a whole flag or its siblings.Serial IDs are zero-based for each org.
0is therefore a real and common value, not an absent one. A test pins this.The Kotlin change removes a shape mismatch. The Android bridge sent an explicit
nullwhen a flag had no serial ID, and the JavaScript decoder omitted the key. Both paths now omit the key.The
@datadog/flagging-corebump suppliesserialIdon the precomputed flag type. The previous~2.0.1pin could not resolve2.1.0.Motivation
The UFC compiler rewrites a holdout into an ordinary allocation before any SDK reads the flag configuration. An exposure event therefore records no holdout. The split serial ID is the only link back to it. An exposure event without the serial ID cannot be resolved to a holdout.
The native SDKs already carry the value, so this completes the Android path. iOS needs a separate change after the iOS SDK bump to
3.17.0.Additional Notes
Testing. 89 tests pass in
src/flags, of which 14 are new. The new tests cover the string form, serial ID0, the six rejected input shapes, sibling isolation, and the value arriving attrackEvaluationon both the offline and the online path. One test asserts the absence of the key on the encoded JSON rather than on the object, because a property set toundefinedis absent from JSON but present on the object.Two mutations confirm the tests fail without the change. Deleting the propagation fails 5 tests. Replacing the type gate with a truthiness check fails 6 tests, including the test for serial ID
0.tsc, ESLint,testDebugUnitTest, and detekt all pass.Risks. The Android online path already sent the serial ID before this PR, because the JavaScript cache stores the native snapshot unchanged. That path now omits the
serialIdkey instead of sendingnullfor a flag with no serial ID. Native reads the key with a null-safe cast, so the exposure event does not change.The
@datadog/flagging-corebump is a minor version of a runtime dependency. It carries three other changes: canonical FFE validation, extended SemVer parsing, and the rules evaluator move. It also removes the transitive@datadog/js-coredependency.