Thoughts on implementing feature toggles #713
Replies: 5 comments 2 replies
-
Ok, I gave it some more thought overnight and I have come up with "documentation first" draft docs for how I imagine the feature toggles feature might work. It does not require splitting up pacts into interactions at the database level, but it would require other database changes. |
Beta Was this translation helpful? Give feedback.
-
Feature Toggles (conceptual "documentation first" docs)OverviewUsing Pact "feature toggles" allows you to safely enable and disable code in your runtime environments in the same way that ConsumerPurposeFor a consumer, Pact feature toggles allow you introduce new Pact interactions into the main branch of your codebase without breaking any builds, and without requiring the use of feature branches. It makes it easy to use Pact with a trunk based workflow. It is important to note that a consumer can only declare feature toggles that are used in the CONSUMER codebase. From a contract perspective, it is none of the consumer's business how the provider rolls out new behaviour. What a consumer is declaring when an interaction is annotated with a feature toggle is "this interaction will only be executed by the consumer when the specified feature toggle is enabled". Keeping that in mind, a consumer cannot declare a feature toggled variation of an existing interaction where only the response changes, as the provider verification code will have no knowledge of the consumer feature toggles. A consumer feature toggle may add a new interaction with a variation of an existing request (which may in turn change the expected response) or add an entirely new request, but there must be something different in the request - not just the response. DeclarationPact.when_consumer_feature_toggle_enabled("alligator_ui_supported") do
animal_service
.given("an alligator exists")
.upon_receiving("a request for an alligator")
.with(
method: :get,
path: '/alligator'
)
.will_respond_with(
status: 200,
headers: { 'Content-Type' => 'application/json' },
body: { name: 'Betty' }
)
end
end BehaviourInteractions that are marked with a feature toggle may fail verification without failing the overall pact verification. When ProviderPurposeTODO (Beth not quite so sure on the value prop for providers) It is important to note that providers can only declare feature toggles that are used in the PROVIDER codebase. DeclarationPact feature toggle hooks allow you to enable and disable feature toggles in your codebase in a similar way to provider state declarations. The feature toggle set up hook will run before the provider state set up hooks, and the tear down hook will run after the provider state tear down hooks. The hooks are designed to dynamically enable/disable the feature toggles in your provider codebase during the Pact verification step, so that it is known what effect each toggle has on the verification results. Pact.feature_toggle "alligator_api_supported" do
set_up do
ENV["ALLIGATOR_API_SUPPORTED"] = "true"
end
tear_down do
ENV["ALLIGATOR_API_SUPPORTED"] = nil
end
end BehaviourEach pact configured in the consumer version selectors will be verified once for each feature toggle, in addition to the base verification which happens with no active feature toggles. The active feature toggle is recorded when publishing each verification result to the Pact Broker. When Enabling feature togglesTo determine whether or not a feature toggle may safely be enabled during runtime, execute the
This command is the same for both consumers and providers. If this command passes succesfully, then you can enable the feature toggle in that environment via your tool of choice, and then mark it as enabled in your Pact Broker.
From then on, any interactions marked with that feature toggle must pass for Disabling feature togglesTo determine whether or not a feature toggle may safely be disabled during runtime, execute the
If it is safe to disable the feature toggle, then you can disable the feature toggle in that environment via your tool of choice, and then mark it as disabled in your Pact Broker.
From then on, any interactions marked with that feature toggle may fail without affecting the results of Deleting feature togglesOnce a feature toggle has been removed from the code, and that code has been deployed, the feature toggle can be deleted from the Pact Broker using the following command.
Example workflow
Pactflow added value features
NotesAn application that is a consumer and a provider could have a feature toggle that affects both its pacts and its verifications. |
Beta Was this translation helpful? Give feedback.
-
Design Assumptions
|
Beta Was this translation helpful? Give feedback.
-
The general premise here seems accurate to me, and the documentation first approach definitely helped clarify your original post. The thoughts that occurred to me are:
I think it would it would be beneficial to collaborate with companies like LaunchDarkly and oss flag platforms like Unleash (I'm sure there are others) to gain better insight into flag usage, especially given the admitted limited experience with the domain. In the case of LD, I think there is missing core functionality, to provide "requisite" hooks (cli/script/webhook) that prevent features from being turned on/off at the core of the product. It is not sufficient to layer these checks behind a custom UI that uses API calls. The rules must be enforced at the core of the toggle product. There is definitely a need for this proposal but the effectiveness will be weakened without core hooks that allow us to block toggle changes. |
Beta Was this translation helpful? Give feedback.
-
Fully disclaimer, I have not worked in a full blown feature toggle environment. I've just done little "turn this code on or off via an environment variable" kind of stuff, which is generally short lived, and managed by developers. I've never used "feature toggle software" and I've never done trunk based development. So I don't feel very qualified to make too many suggestions over how to implement this.
However. These are the thoughts that I have. Note that I'm not very certain about any of them 😆
I think what both a consumer and a provider would want from a Pact implementation of "feature toggles" is the answer to the question "if I enable/disable this feature toggle, will everything still work?".
I think that the "can I enable" question should only ever be relevant to YOUR OWN CODE. As in, I don't think a consumer should be able to specify feature toggles that relate to provider behaviour. I feel like from the provider's side, that's an implementation detail. The consumer shouldn't need to know the provider's feature toggles. How the provider rolls out or toggles functionality shouldn't matter to the consumer. All it should know is whether the interactions it expects are currently supported or not.
I think this is going to confuse and possibly anger users however 😆 I can see this being a tricky feature to both implement and understand.
So, for a consumer the pertinent question might be, "If I enable feature X in the consumer codebase, will the interactions that I require for feature X be supported by the providers?". For a provider it might be, "If I enable feature toggle Y, will I break any consumers?" Or perhaps more importantly, "Crap, we've discovered a problem with this feature, if I disable feature toggle Y, will we break any existing consumers?"
From a consumer's perspective, a feature toggle might change the way an existing a request is made, or it might add a new interaction. The variation should only be in relation to the request.
From a provider's perspective, a feature toggle might change/add a support for a new interaction. The variation should only be in the response. Hopefully in a backwards compatible way, but maybe not. For example, if someone is doing a "change an attribute by adding a new one and then removing the old one" they might have that under a feature toggle. They might want to know if they can enable the feature toggle that removes the old attribute from the response.
So, I'm imagining either an extension of can-i-deploy that takes feature toggle options, or a new can-i-toggle. It may depend whether the toggle is a build/deploy time concern or a runtime concern. Perhaps we need both, because both methods of using feature toggles are valid. I feel like this is a command that is likely to be run ad-hoc, but people, rather than machines, as I think enabling/disabling a feature toggle gets done when a person decides to do it, rather than by a scheduled/automated process. It is probably therefore a good candidate for implementing in a UI in Pactflow.
I'm unsure whether there would need to be explicit support for on and off states of a feature toggle, or whether the base state is off, and we only support specifying toggles that are on 🤷🏽♀️
One of the big questions is - does a feature toggle belong at a pact level or an interaction level. Feature toggles must, by the nature of the way the tool works, support a multiplicity of behaviours, and that either needs to be represented by a multiplicity of pacts, or a multiplicity of interactions. My gut feel is that it should probably be at the interaction level, but I haven't spent a heap of time hashing this out in my head yet.
I think from the consumer side, feature toggles would most elegantly be specified as just another part of the interaction declaration - a structured way of doing a "given".
From the provider side, a feature toggle would somehow have to be associated with an interaction+verification result. Perhaps for each variation of feature toggle, all the pacts get verified each time? I'm really not sure. I don't know how you'd allow the provider to specify which verifications were relevant to each feature toggle. Maybe feature toggles don't even make sense from a provider perspective at all.
This leads me to a thought that I've had for a long time. I suspect, to implement this feature elegantly, we'll want to start recording verification results at the interaction level, not the pact level. I have been thinking that having an "all in one" verification where all the unique interactions were grouped into an uber-pact and verified at once would be much more efficient than verifying one pact at a time. Then the results could be posted back, and the Pact Broker could divvy back out the results for each pact. I had a go at implementing a POC a few years ago, but never finished it.
You'd need to introduce an interactions table, and have a many to many relationship between pacts and interactions, and a many to many between interactions and verification results, which would link back to provider versions.
Adding this level of knowledge about the internal structure of a pact is something I have deliberately avoided so far, so as to keep the Pact Broker agnostic of the contract type, and make it re-usable across other tools. That hasn't eventuated however, and, like my previous realisation that branches/deployments provided a much better experience for users than process agnostic tags, I now suspect that leaning into a fully pact aware broker might do the same.
It's my opinion that feature toggles would have to be an OSS Pact Broker feature, because the code to support it is going to be inextricably tied to the pact/verification domain models, and will required a fair amount of change, and I cannot see a way of neatly extending Pactflow only with this functionality.
Beta Was this translation helpful? Give feedback.
All reactions