-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[FeatureFlag] Propose a simple version #53213
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
base: 7.3
Are you sure you want to change the base?
Conversation
I like this simplified version, which is all, I would need in the first step ❤️ Thank you! |
src/Symfony/Component/FeatureFlag/DataCollector/FeatureCheckerDataCollector.php
Outdated
Show resolved
Hide resolved
src/Symfony/Component/FeatureFlag/DataCollector/FeatureCheckerDataCollector.php
Outdated
Show resolved
Hide resolved
src/Symfony/Component/FeatureFlag/DataCollector/FeatureCheckerDataCollector.php
Outdated
Show resolved
Hide resolved
src/Symfony/Component/FeatureFlag/DataCollector/FeatureCheckerDataCollector.php
Outdated
Show resolved
Hide resolved
In the going further section Gitlab is mentioned. The Gitlab feature flag is based on and compatible with the Unleash api spec. So maybe it is worth to implement that api since a few services are compatible with it. |
@noahlvb : yes we have this in mind and plan on implementing :
and more if further contributions. |
Bridges to providers should be proposed in a follow-up PR |
04de789
to
0c4aec4
Compare
About the proposed API, I have some suggestions:
If we take the stricter path of the concept, a feature flag should deal only with a boolean result at the end. So, I don't think the
Following the same principle, the toggle callable should always return a boolean value, receiving the arguments necessary to compute it. Thus, we can pass all arguments from Example: $features = new FeatureRegistry([
'feature_version' => fn (int $v): bool => random_int(1, 3) === $v,
]);
$checker = new FeatureChecker($features);
if ($checker->isEnabled('feature_version', 1)) {
// ...
} We could also add This way feature deciders (the callables here) have full control over the computation of the feature value, and the feature checker will be responsible for checking/caching the value only. |
On my side I think it's important to support non-boolean values from day 1. |
src/Symfony/Component/FeatureFlag/Exception/FeatureNotFoundException.php
Outdated
Show resolved
Hide resolved
src/Symfony/Component/FeatureFlag/Debug/TraceableFeatureChecker.php
Outdated
Show resolved
Hide resolved
src/Symfony/Component/FeatureFlag/Debug/TraceableFeatureChecker.php
Outdated
Show resolved
Hide resolved
Co-authored-by: Adrien Roches <adrien@roc-it.tech>
47d16ba
to
2f490f8
Compare
66c5c73
to
802cd2c
Compare
@Jean-Beru What do you think of the approach used in |
Simple FeatureFlag component
Introduction
Co-authored with @Neirda24 who is also at the origin of this component.
Based on the initial pull request #51649:
Currently, there is no straightforward method to conditionally execute parts of the source code based on specific
contexts. This PR tries to solve this issue by integrating some easy way to check is this or that feature should be
enabled.
First check out Martin Fowler's article about different use cases for feature flag (a.k.a. feature toggling).
He categorizes feature flag like this:
or control when a feature is released.
example it is common to deactivate certain features for Ops because the load will be on other pages.
There are already some libraries / bundles out there but they either lack extensibility or are locked in to a SAAS
tool (Unleash, Gitlab (which uses Unleash), ...).
Proposal
The FeatureFlag component provides a service that checks if a feature is enabled. A feature is a callable which returns
a value compared to the expected value to determine is the feature is enabled (mostly a boolean but not limited to).
Since every declared feature is a callable, they are only resolved when needed.
The component
A service implementing the
ProviderInterface
is responsible for giving a callable from a feature name. This callable,which does not accept any argument (for now), can be called to get the feature value.
The
FeatureChecker
is responsible for checking if a feature is enabled from its name. It uses aProviderInterface
toretrieve the corresponding feature, resolves it and compare it to the expected value (
true
by default).For now, two providers are currently implemented:
InMemoryProvider
which contains the features passed in its constructorChainProvider
which aggregates multiple providers and iterates through them to retrieve a feature. It allowscombining different sources of feature definitions, such as in-memory, database, or external services.
Usage
Declare your features using a provider and pass it to the checker:
Of course, invokable classes can be used:
Check the feature's value:
Once the feature's value is resolved, it is stored to make it immutable:
FrameworkBundle integration
In addition, the
#[AsFeature]
attribute can be used to make a feature from a service:In fact, any callable can be declared as a feature using this attribute.
Thanks to the ExpressionLanguage component, feature flags can be used in Route definitions.
WebProfilerBundle integration
The Feature Flag component integrates with the WebProfilerBundle to provide insights into feature evaluations:
Resolved features in the toolbar:
Resolved features in the panel:
Unresolved features in the panel:
Disabled FeatureFlag menu in the panel:
TwigBridge integration
Feature flags can be checked in Twig template if needed.
Going further (to discuss in dedicated PRs)
LaunchDarkly, etc.) by adding a dedicated provider
#[Feature]
to restrict access to a controllerdebug:feature
to make feature debugging easierAbout caching
Immutability
The resolved value of a feature must be immutable during the same request to avoid invalid behaviors. That's why the
FeatureChecker
keeps values internally.Heavy computing
Even if features are lazy loaded, they can be resolved in every request to determine if a controller can be accessed. It
could be problematic when the provider retrieves this value from a database or an external service. Depending on the
solution chosen to store features, the logic to cache value may differ. That's why the value caching between requests
should be handled by providers.
For instance, Unleashed can define some strategy configuration that can be cached and used to resolve feature values.
See https://github.com/Unleash/unleash-client-php/tree/main/src/Strategy.