Skip to content

docs: add numeric coercion proposal#1979

Open
toddbaert wants to merge 3 commits into
mainfrom
docs/numeric-coercion-adr
Open

docs: add numeric coercion proposal#1979
toddbaert wants to merge 3 commits into
mainfrom
docs/numeric-coercion-adr

Conversation

@toddbaert

@toddbaert toddbaert commented Jun 8, 2026

Copy link
Copy Markdown
Member
  • defines numeric coercion expectations
  • defines limits for numeric flags (IEEE 754 safe int limit)

Fixes: #1978

* handles numeric coercion
* defines limits for numerics

Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
@toddbaert toddbaert requested review from a team as code owners June 8, 2026 20:46
@netlify

netlify Bot commented Jun 8, 2026

Copy link
Copy Markdown

Deploy Preview for polite-licorice-3db33c ready!

Name Link
🔨 Latest commit 9ee1e9a
🔍 Latest deploy log https://app.netlify.com/projects/polite-licorice-3db33c/deploys/6a280dc2150b0900081f08fb
😎 Deploy Preview https://deploy-preview-1979--polite-licorice-3db33c.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@dosubot dosubot Bot added the size:M This PR changes 30-99 lines, ignoring generated files. label Jun 8, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new Architecture Decision Record (ADR) establishing a consistent numeric coercion contract for typed flag accessors across all flagd implementations. It proposes allowing coercion only when lossless and capping numeric values within the IEEE-754 safe-integer range to prevent silent truncation, overflow, or precision loss. Feedback on the ADR suggests improving mathematical precision in the proposal table by explicitly referencing the "safe-integer range" rather than just "2^53-1" to clearly account for negative bounds.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +64 to +65
| int, exceeds the maximum of the numeric resolver in use, but within 2^53-1 | `TYPE_MISMATCH` | value (widened) |
| int, exceeds 2^53-1 | rejected at load (or `PARSE_ERROR` at evaluation if not validated) | rejected at load (or `PARSE_ERROR` at evaluation if not validated) |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For mathematical precision, it is better to refer to the safe-integer range [-(2^53 - 1), 2^53 - 1] rather than just 2^53-1 or within 2^53-1, as negative integers can also exceed the safe range in absolute value (e.g., -9007199254740992). Using "outside the safe-integer range" or "within the safe-integer range" makes this explicit.

Suggested change
| int, exceeds the maximum of the numeric resolver in use, but within 2^53-1 | `TYPE_MISMATCH` | value (widened) |
| int, exceeds 2^53-1 | rejected at load (or `PARSE_ERROR` at evaluation if not validated) | rejected at load (or `PARSE_ERROR` at evaluation if not validated) |
| int, exceeds the maximum of the numeric resolver in use, but within the safe-integer range | <code>TYPE_MISMATCH</code> | value (widened) |
| int, outside the safe-integer range <code>[-(2^53 - 1), 2^53 - 1]</code> | rejected at load (or <code>PARSE_ERROR</code> at evaluation if not validated) | rejected at load (or <code>PARSE_ERROR</code> at evaluation if not validated) |

Comment on lines +58 to +59
flagd additionally caps numeric flag values at the IEEE-754 safe-integer range, `[-(2^53 - 1), 2^53 - 1]`.
Variants whose absolute value exceeds this range are considered invalid per the JSON schema (we'll add this limit there).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is somewhat opinionated, but for now I think I'd rather define a hard cap than say we'll possibly lose precision at some integer value. This number is sufficiently high, IMO, it can support extremely large values such as 8 petabytes (in bytes), or timestamps in millis.


## Considered options

1. **Lossless coercion only, with a hard cap at the JSON-safe integer range (2^53 - 1)**: an accessor returns a value if and only if the conversion is lossless.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it the JSON max safe integer or the JavaScript max safe integer? From what I read into the spec, there is no max safe int (https://www.json.org/json-en.html) in JSON. I think we should use the wording IEEE 754 64-Bit max safe integer instead, because this limitation does not only apply to JavaScript, as it is done elsewhere in this document

@toddbaert toddbaert Jun 9, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, this is mostly a JS not a JSON limitation. The reason it involves JSON is because many JSON parsers (including JS's) don't support parsing integers larger than the JS number in question, even some Java parsers by default have this issue (Gson I believe) so it gets a bit messy.

But I fully agree that the IEEE 754 64-Bit max safe integer is a better way to talk about this number so I will use that naming (I have in other places in the doc already).

| int, exceeds the maximum of the numeric resolver in use, but within 2^53-1 | `TYPE_MISMATCH` | value (widened) |
| int, exceeds 2^53-1 | rejected at load (or `PARSE_ERROR` at evaluation if not validated) | rejected at load (or `PARSE_ERROR` at evaluation if not validated) |
| float, whole-valued and within the resolver's int range (e.g. `10.0`) | value (e.g. `10`) | value |
| float, fractional or out of the resolver's int range | `TYPE_MISMATCH` | value |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is may be an inconsistency: IIRC, we do not distinguish numbers in the flagd config per type (int vs float), we just have a Number type.
With this rule, however, fetching a flag that evaluates to 2^53 + 1 (int) would fail no matter if it is fetched as int or float, but 2^53 + 1.1 (float) would work when fetched as float. I think that all values that fit inside an IEEE 754 64-Bit max safe integer should be safely returned when fetching a float flag.

@toddbaert toddbaert Jun 9, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is may be an inconsistency: IIRC, we do not distinguish numbers in the flagd config per type (int vs float), we just have a Number type.

We don't in the spec, but languages and flagd does (in terms of how the resolvers behave).

With this rule, however, fetching a flag that evaluates to 2^53 + 1 (int) would fail no matter if it is fetched as int or float, but 2^53 + 1.1 (float) would work when fetched as float. I think that all values that fit inside an IEEE 754 64-Bit max safe integer should be safely returned when fetching a float flag.

I think my table was not clear; it's mostly about what happens if you evaluate a fractional decimal number with some kind of int resolver. For float evaluations themselves - I think since all languages support IEEE-754 we don't need to say much. 2^53 + 1.1 from your example is specifically classed as out-of-bounds on the config level by this proposal, though the table didn't explicitly mention that (it was just mentioned in prose elsewhere). I've included that now, but the fetched as float column is pretty unsurprising and uninteresting.

flagd additionally caps numeric flag values at the IEEE-754 safe-integer range, `[-(2^53 - 1), 2^53 - 1]`.
Variants whose absolute value exceeds this range are considered invalid per the JSON schema (we'll add this limit there).

| Variant kind | Fetched as Integer | Fetched as Float |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do we treat values that exceed even the maximum finite number that can be represented with IEEE 754 64-Bit? I think we should call out explicitely that we will reject those, since they can be represented in JSON

@toddbaert toddbaert Jun 9, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm confused, but this proposal caps numeric flags at the iEEE 754 max safe int:

flagd additionally caps numeric flag values at the IEEE-754 safe-integer range, [-(2^53 - 1), 2^53 - 1].

This would make it invalid to configure such a value; what happens if the config validation is ignored can be decided here, but I would suggest PARSE_ERROR.

| ------------------------------------------------------------- | ------------------ | ----------------- |
| int, fits the maximum of the numeric resolver in use | value | value (widened) |
| int, exceeds the maximum of the numeric resolver in use, but within 2^53-1 | `TYPE_MISMATCH` | value (widened) |
| int, exceeds 2^53-1 | rejected at load (or `PARSE_ERROR` at evaluation if not validated) | rejected at load (or `PARSE_ERROR` at evaluation if not validated) |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really like the idea that we reject values that we could still safely represent. E.g. in Java, we do have the option to represent 64 bit integers, but we would still reject the numbers between 2^53 and 2^64 for no apparent reason for a Java developer.
Should we in this case refer to the used programming language? I.e. if no such value exists due to limitations of the language, e.g. JavaScript, we return PARSE_ERROR, else we return the value. Programmers would already know which numbers their languages can handle and which it cannot.

@toddbaert toddbaert Jun 9, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Programmers would already know which numbers their languages can handle and which it cannot.

Programmers do, but I think there's a desire for cross languages consistency - and for that reason I feel that putting an upper bound on what flagd supports is a good idea. For OpenFeature itself, I agree with you: defer to the language - but I would like to assure users that flagd operates consistent for numeric values across all languages, event if it involves a hard cap, personally.

Imagine this real use case:

As a PM, I want to define the amount in of data in bytes that a customer can store for some purpose

I don't want the PM to have to consider the numeric types of Python/Java/JS - I just want the configuration plane to give them solid feedback. If we aren't consistent here, it's likely that a some validation that passes in the UI doesn't work in the backend, or some batch job, because of precision loss. If we prevent the configuration of such values in the first place, it seems like a good trade-off to me. (edited)

Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
@toddbaert toddbaert requested a review from chrfwow June 9, 2026 12:36
Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. and removed size:M This PR changes 30-99 lines, ignoring generated files. labels Jun 9, 2026
@sonarqubecloud

sonarqubecloud Bot commented Jun 9, 2026

Copy link
Copy Markdown

@toddbaert toddbaert requested a review from erka June 9, 2026 14:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] Define numeric coercion behavior across flagd implementations

3 participants