Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add flag-evaluation spec, Makefile, initial spec structure #35

Merged
merged 2 commits into from
Apr 27, 2022

Conversation

toddbaert
Copy link
Member

@toddbaert toddbaert commented Apr 21, 2022

First pass at a partial spec, based on cumulative findings from our research repo. Only the Evaluation API is proposed (specification/flag-evaluation/flag-evaluation.md, also note the associated assertion JSON file: specification/flag-evaluation/flag-evaluation.json). The best way to view the actual spec is probably to view the branch directly, since it's mostly markdown: https://github.com/open-feature/spec/tree/spec/flag-evaluation-api/specification

One of the goals was to design the structure of the spec, so I've included stubs for some of the other parts.

Please also checkout the specification/README.md, which contains an overview, conformance clause, and document statuses.

For ease of generating the JSON assertion files, I've also added a very simple Makefile. If you're interested in looking at spec only, please make use of the Github's file-type filter:
image

UPDATE:

I believe I've addressed all the concerns. I've also opened: #36 in response to comments about CI and various helpful automation suggestions. Please remember, everything in the spec at the moment is still status: experimental, so don't think that anything here is final!

Comment on lines 101 to 103
> The `client` method for flag activity evaluation **MAY** be semantically different from the boolean flag evaluation method.

Some providers may draw a distinction between boolean flag value evaluation, and flag activity. Put another way, a flag may be "disabled", but simultaneously have an associated auxillary boolean payload of `true`.
Copy link
Member Author

@toddbaert toddbaert Apr 21, 2022

Choose a reason for hiding this comment

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

Please note this. It seems like the optimal way to deal with (or rather, not deal with) this "flag-value/flag activity" impedance mismatch across providers.

Copy link
Member

Choose a reason for hiding this comment

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

This seems surprising to me. This single API will tell you "Is this enabled in the UI" and also "is the flag evaluating to true"? Why complecting these two concerns?

Choose a reason for hiding this comment

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

Is there additional information around this? I'm having some trouble parsing the use case that isn't handled internally in the provider.

Copy link
Member Author

@toddbaert toddbaert Apr 21, 2022

Choose a reason for hiding this comment

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

@justinabrahms

This single API will tell you "Is this enabled in the UI" and also "is the flag evaluating to true"?

Not quite (unless I'm misunderstanding you)

The issue is that some providers allow logically boolean flags (enabled/disabled) WITH additional associated boolean payloads (every flag has a bool and an optional number/string/boolean payload). Because some providers work that way, I think it forces us to have this kind of resolution - we need a method for the boolean flag, and a separate method to get it's associated boolean payload.

In providers that DONT have this concept (most of them), isEnabled() and getBooleanValue() do the same thing, isEnabled() is just syntactic sugar.

I'm open to other solutions to the problem though.

Copy link
Member Author

@toddbaert toddbaert Apr 21, 2022

Choose a reason for hiding this comment

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

@InTheCloudDan

Is there additional information around this? I'm having some trouble parsing the use case that isn't handled internally in the provider.

See: https://github.com/open-feature/spec/pull/35/files#r855378994. It is really internally handled by the provider, but I wanted to call it out here because it's relevant to the expected behavior of the evaluation API (we can remove it if it's confusing and only include it in the provider spec).

Maybe this proposal isn't the best solution, but @dabeeeenster has brought the issue up a few times, and this is the best I can come up with. Open to more suggestions though.

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm thanks for the clarification. The wording here makes more sense with this in mind.

It was supporting this that I was attempting to do, but after this discussion, I'm wondering if it's not better to completely remove the isEnabled() method entirely, and let the Flagsmith provider determine how to handle resolving getBooleanValue in their case.

I'm supportive of this tbh. Seems very provider-specific

Copy link
Member

Choose a reason for hiding this comment

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

I'm in favor of removing isEnabled(). In my opinion, it seems very unlikely that an application author would expect isEnabled() and getBooleanValue() to behave differently. Removing it would eliminate that confusion.

Copy link
Contributor

Choose a reason for hiding this comment

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

flagsmith.getValue('toddsFlag') which can return a boolean completely independent of the returned value of hasFeature above, true or false. @dabeeeenster please correct me here, if I'm wrong.

Correct.

This is something of a hangover from pure feature flags and a more complete flag and remote config solution. We wanted to give people a simple boolean state for a flag but then be able to enhance it with essentially a general String value, but then what if they store True in the string value, and how does that typing get supported through all the SDKs and their various type handling.

Hard to say what the best general solution is here. I think if the OpenFeature SDK can provide its best interpretation of the intention from the provider's interface but let developers have access to the base data is probably the optimal solution.

I believe most vendors have a similar approach of booleans and additional config/multi-variate values but I cant (and don't want to !) speak for them.

Copy link
Member

Choose a reason for hiding this comment

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

If this functionality is there due to historical reasons, I propose that we do not add this to the spec. I think the expected behavior for OpenFeature application developers would be that a disabled feature flag does not have any meaningful auxiliary value. In this case, it would simply return the default value provided during the evaluation. However, the provider pattern is flexible enough to allow each vendor to decide how best to implement the interface. In this case, the getBooleanValue() could get the value from the state of the flag or from the getValue() method.

Copy link
Member Author

Choose a reason for hiding this comment

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

For now, I've removed the isEnabled() and isEnabledDetails() methods. We can always open up a new issue for this if we think it's something that's absolutely necessary, but I agree that the provider interface (spec to come) gives enough flexibility to address this in numerous ways.

Comment on lines 142 to 152
##### Requirement 1.16

> In cases of abnormal execution (network failure, unhandled error, etc) the `reason` field in the `evaluation details` **MUST** be set to `"ERROR"`.

##### Requirement 1.17

> The `evaluation details` wrapper **MUST** have an optional `error code` field of type string, which, in cases of abnormal execution, identifies an error occurred during flag evaluation, with possible values `"PROVIDER_NOT_READY"`, `"FLAG_NOT_FOUND"`, `"PARSE_ERROR"`, `"TYPE_MISMATCH"`, or `"GENERAL"`.

##### Requirement 1.18

> If the `reason` in the `evaluation details` is set to `"ERROR"`, the sibling `error code` field **MUST** be set.
Copy link
Member Author

@toddbaert toddbaert Apr 21, 2022

Choose a reason for hiding this comment

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

These are all dependent on the outcome of the discussion here: #32 (comment)

Copy link
Contributor

Choose a reason for hiding this comment

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

Error code was quite a contentious topic in opentelemetry which we went back and forth on for a long time. I would think about what usecase you're trying to solve by having an error code and see if you can solve it in a more explicit way. Maybe a "retryable" boolean?

Copy link
Member

Choose a reason for hiding this comment

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

The main use cases we're focusing on is telemetry and debugging.

Copy link
Member Author

Choose a reason for hiding this comment

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

@dyladan What @beeme1mr says is correct. We believe it to be unlikely that anyone would write code based on this value. It's mostly useful for debugging purposes, and for monitoring the performance and availability underlying flagging system.

@toddbaert toddbaert changed the title Add flag-evaluation spec, Makefile Add flag-evaluation spec, Makefile, initial spec structure Apr 21, 2022
@toddbaert
Copy link
Member Author

We also may want a section that specifically mentions that we encourage idiomatic use of language features, and that prescriptions that imply certain unavailable language features should be tolerated.

Co-authored-by: Diego Hurtado <[email protected]>
@toddbaert toddbaert force-pushed the spec/flag-evaluation-api branch from b332068 to a782d67 Compare April 21, 2022 16:04
specification/flag-evaluation/flag-evaluation.md Outdated Show resolved Hide resolved
specification/flag-evaluation/flag-evaluation.md Outdated Show resolved Hide resolved
specification/flag-evaluation/flag-evaluation.md Outdated Show resolved Hide resolved
Comment on lines 101 to 103
> The `client` method for flag activity evaluation **MAY** be semantically different from the boolean flag evaluation method.

Some providers may draw a distinction between boolean flag value evaluation, and flag activity. Put another way, a flag may be "disabled", but simultaneously have an associated auxillary boolean payload of `true`.
Copy link
Member

Choose a reason for hiding this comment

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

This seems surprising to me. This single API will tell you "Is this enabled in the UI" and also "is the flag evaluating to true"? Why complecting these two concerns?

specification/flag-evaluation/flag-evaluation.md Outdated Show resolved Hide resolved
specification/flag-evaluation/flag-evaluation.md Outdated Show resolved Hide resolved
specification/flag-evaluation/flag-evaluation.md Outdated Show resolved Hide resolved
specification/flag-evaluation/flag-evaluation.md Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
specification/flag-evaluation/flag-evaluation.md Outdated Show resolved Hide resolved
specification/flag-evaluation/flag-evaluation.md Outdated Show resolved Hide resolved
specification/flag-evaluation/flag-evaluation.md Outdated Show resolved Hide resolved
Comment on lines 101 to 103
> The `client` method for flag activity evaluation **MAY** be semantically different from the boolean flag evaluation method.

Some providers may draw a distinction between boolean flag value evaluation, and flag activity. Put another way, a flag may be "disabled", but simultaneously have an associated auxillary boolean payload of `true`.

Choose a reason for hiding this comment

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

Is there additional information around this? I'm having some trouble parsing the use case that isn't handled internally in the provider.

Copy link
Contributor

@dyladan dyladan left a comment

Choose a reason for hiding this comment

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

I did not review the parser at all, but I looked pretty thoroughly at the spec. In general, I would characterize it as a bit too prescriptive and doesn't leave room for the idioms of different languages to be used in some places. This may lead to an API implemented in one language which feels like it doesn't really belong. Take all my suggestions as merely suggestions as I am an outsider here.

README.md Show resolved Hide resolved
specification/README.md Show resolved Hide resolved
specification/flag-evaluation/flag-evaluation.md Outdated Show resolved Hide resolved
specification/flag-evaluation/flag-evaluation.md Outdated Show resolved Hide resolved
specification/flag-evaluation/flag-evaluation.md Outdated Show resolved Hide resolved
specification/flag-evaluation/flag-evaluation.md Outdated Show resolved Hide resolved
specification/flag-evaluation/flag-evaluation.md Outdated Show resolved Hide resolved
specification/flag-evaluation/flag-evaluation.md Outdated Show resolved Hide resolved
specification/flag-evaluation/flag-evaluation.md Outdated Show resolved Hide resolved
@toddbaert toddbaert force-pushed the spec/flag-evaluation-api branch from b429241 to 3635dc5 Compare April 21, 2022 19:31
@toddbaert toddbaert requested a review from agentgonzo April 25, 2022 13:34
@toddbaert toddbaert force-pushed the spec/flag-evaluation-api branch from 173cafc to 381c7ba Compare April 25, 2022 13:45
>
> ##### Conditional Requirement 1.8.1
>
> > The `client` **MUST** provide methods for typed flag evaluation, including boolean, numeric, string, and structure.
Copy link
Member

Choose a reason for hiding this comment

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

with these examples, it might be nice to provide an example that uses generics to avoid needing the type in the name, for example string myString = client.getValue<string>('string-flag', 'default');

Copy link
Member Author

@toddbaert toddbaert Apr 25, 2022

Choose a reason for hiding this comment

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

This conditional requirement is really about using a type-aware function:

if:

The language type system differentiates between strings, numbers, booleans and structures.

then:

The client MUST provide methods for typed flag evaluation, including boolean, numeric, string, and structure.

Because some of our underlying providers have type safe methods (see the cloudbees provider) and so we can enforce type safety at runtime, I don't think we actually want a generic method in these cases. In the case of TS/JS in particular, the fact there's no type-safety at runtime causes a real problem. It leaves us in a situation where the only way to know which method to call on the underlying provider (.value() (string) vs .getNumber()(number), for instance) is to test the type of the detaultValue (but this could be passed as the wrong type in error). Actually, we may have similar problems in Java due to type erasure of generics.

See: https://github.com/open-feature/sdk-research/blob/main/packages/js-cloudbees-provider/src/lib/js-cloudbees-provider.ts

In summary, I think we need type-specific and type-safe methods like getBooleanValue() and therefor the kind of thing you suggest might be a bit redundant, and behave unpredictably, particularly in JS and languages where the generic type information is erased at runtime.


##### Requirement 1.9

> The `client` **MUST** provide methods for detailed flag value evaluation with the value wrapped in a `evaluation details` structure, with parameters `flag key` (string, required), `default value` (boolean | number | string | structure, required), `evaluation context` (optional), and `evaluation options` (optional).
Copy link
Member

Choose a reason for hiding this comment

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

should this list include reason and error code - those are referenced in Requirement 1.14 and Requirement 1.16 lower down.

Copy link
Member Author

@toddbaert toddbaert Apr 25, 2022

Choose a reason for hiding this comment

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

Again I've been ambiguous 😅 ... these are the parameters to the method, not the fields in the structure. I will rephrase to make that more clear. The structure is detailed in types.md, as well as below.

Copy link
Member Author

Choose a reason for hiding this comment

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

rephrased, let me know if that's clearer.


##### Requirement 1.9

> The `client` **MUST** provide methods for detailed flag value evaluation with the value wrapped in a `evaluation details` structure, with parameters `flag key` (string, required), `default value` (boolean | number | string | structure, required), `evaluation context` (optional), and `evaluation options` (optional).
Copy link
Member

Choose a reason for hiding this comment

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

wouldn't they be called fields on the evaluation details structure, rather than parameters?

Copy link
Member Author

Choose a reason for hiding this comment

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

These are the parameters to the method, not the fields in the structure. I will rephrase to make that more clear.

Copy link
Member Author

Choose a reason for hiding this comment

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

rephrased, let me know if that's clearer.

specification/glossary.md Outdated Show resolved Hide resolved
README.md Show resolved Hide resolved
specification/flag-evaluation/flag-evaluation.md Outdated Show resolved Hide resolved
### structure

Structured data, presented however is idiomatic in the implementation language, such as JSON or YAML.

Copy link
Member

Choose a reason for hiding this comment

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

evaluation context is missing from this section

Suggested change
### evaluation context
Structured data, presented however is idiomatic in the implementation language, such as JSON or YAML.

Copy link
Member Author

@toddbaert toddbaert Apr 26, 2022

Choose a reason for hiding this comment

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

I think we need to take some time to flesh out the Evaluation Context a bit better. Some flag systems have pre-defined fields, and we'll need to define some too. There's also the implicit language specific context-injection and propogation mechanisms that need to be mentioned (Go Context, JS async-local-storage, etc). I think the context probably requires it's own spec page. You'll see this PR actually has a stub md file for it: specification/evaluation-context/evaluation-context.md

Copy link
Member

@beeme1mr beeme1mr left a comment

Choose a reason for hiding this comment

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

It looks like the major issues have been addressed now. I think it's time to merge it as is since nothing here is final. Any issues can be handled with a tickets and/or a PR.

Co-authored-by: Justin Abrahms <[email protected]>
Co-authored-by: Daniel Dyla <[email protected]>
Co-authored-by: Pete Hodgson <[email protected]>
Co-authored-by: Steve Arch <[email protected]>
Co-authored-by: Michael Beemer <[email protected]>
@toddbaert toddbaert force-pushed the spec/flag-evaluation-api branch from c76b77c to 4fb5705 Compare April 27, 2022 01:51
@toddbaert
Copy link
Member Author

Rebased, squashed, added co-authors.

@toddbaert toddbaert merged commit 062a083 into main Apr 27, 2022
@toddbaert toddbaert deleted the spec/flag-evaluation-api branch April 27, 2022 01:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants