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

feat: dependent features #63

Merged
merged 6 commits into from
Sep 21, 2023
Merged

feat: dependent features #63

merged 6 commits into from
Sep 21, 2023

Conversation

kwasniew
Copy link
Contributor

@kwasniew kwasniew commented Sep 20, 2023

About the changes

Spec for dependent features

Data structure for dependent features:

  • depend on parent being enabled: {feature: 'parent'}
  • depend on parent being disabled (useful for kill-switches): {feature: 'parent', enabled: false}
  • depend on parent resolving to a value/variant: {feature: 'parent', variants: ['parentVariant', 'anotherParentVariant']}

We're deliberately choosing feature as a key to have an option for other types of dependencies e.g. strategies

Most of the code has the same activation strategy with a strategy variant. Enabled tests and variant tests are almost identical (only description and expected result are different).

Interesting cases:

  • you can depend on many features (with AND operator between them)
  • when you depend on a parent feature variant you can specify a list of options that you'd like to match against (OR operator between variants)
  • we don't support transitive dependencies, only direct dependencies

Reference implementation passing spec tests: Unleash/unleash-client-node#520

Important files

Discussion points

"description": "Parent does not exist",
"context": {},
"toggleName": "parents.not.exist.child.enabled",
"expectedResult": false
Copy link
Member

Choose a reason for hiding this comment

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

Nice, that's what I would have done too, I think this is correct. This is a big decision though. It means stale caches and/or cycling out your api key can potentially change the state of a toggle. Leaving this here for visibility in case someone has a different opinion

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Glad you're confirming my confirmation bias :) As you said it's up for a discussion.

Copy link
Contributor

Choose a reason for hiding this comment

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

This is tricky. Since you can filter by tags it's technically possible to end up in a situation where the parent is not present. We'll need to implement safeguards in the SDK to warn you if this happens, but I think the behavior here is correct. If you depend on a feature toggle that you have not fetched, there is no way for us to know the evaluation result of that toggle and we can not determine that the child should be enabled.

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess this is where a feature toggle type would come in handy @kwasniew

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@FredrikOseberg I will try to add a warning when you try to depend on a feature that you don't have in the SDK

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@FredrikOseberg can you elaborate on a feature toggle type?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes. Like we talked about in the meeting, some way to mark a toggle as dependable, we discussed the possibility of having a new feature toggle type called Dependable Toggle. If we had that, we could circumvent the filtering in the API and say that "No, we don't care that you are filtering by tags, you'll get all the dependable toggles in this project regardless".

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@FredrikOseberg added reference impl for warnings: Unleash/unleash-client-node@1ed0994

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Now I remember the dependent feature markers we talked about :) We definitely need to keep it in mind. For this spec though I think we can proceed without this concept since it will only affect the client API not this spec.

Copy link
Contributor

Choose a reason for hiding this comment

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

Agree. This spec just surfaced the problem, so I just mentioned it here.

Copy link
Member

@sighphyre sighphyre left a comment

Choose a reason for hiding this comment

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

I think it's worth including some tests that also depend on context properties, e.g. a constraint. Based on the node implementation the desired behaviour appears to be that the context is reused for the parent check as well. Worth enforcing here I think

@kwasniew
Copy link
Contributor Author

@sighphyre addressed all your points :)

Copy link
Member

@sighphyre sighphyre left a comment

Choose a reason for hiding this comment

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

What a legend. This looks rather nice

I think others should weigh in but LGTM

"enabled": true,
"dependencies": [{
"feature": "parent.with.variant",
"variants": []
Copy link

@daveleek daveleek Sep 21, 2023

Choose a reason for hiding this comment

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

This operates on returned variant results I take it? Do default-variants count as []?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

with variants: [] you don't check variants, only parent being enabled. It's the same as not providing variants at all.

with variants: ['disabled'] you check default variant.

This is reference impl: parent.variants.includes(this.getVariant(parent.feature, context).name)

If we think that it's a good idea to do it this way I can go ahead and add it to the spec.

Choose a reason for hiding this comment

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

Ah, great! So "variants": [ ] is the same as not including the variants property in the dependency

Copy link

@daveleek daveleek left a comment

Choose a reason for hiding this comment

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

I like it!

@kwasniew kwasniew merged commit b7b4bca into main Sep 21, 2023
@kwasniew kwasniew deleted the dependent-feature-toggles branch September 21, 2023 09:50
gardleopard added a commit to Unleash/unleash-client-ruby that referenced this pull request Oct 16, 2023
This pr implements dependant feature toggles. The tests are specified in
the client specifications Unleash/client-specification#63
gardleopard added a commit to Unleash/unleash-client-ruby that referenced this pull request Oct 16, 2023
* feat: dependant toggles

This pr implements dependant feature toggles. The tests are specified in
the client specifications Unleash/client-specification#63
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.

4 participants