- DE
- Services
- Our service portfolio
We bring your digital product vision to life, from crafting real-world testable prototypes to delivering comprehensive product solutions.
- Collaboration models
Explore collaboration models customized to your specific needs: Complete nearshoring teams, Local heroes from partners with the nearshoring team, or Mixed tech teams with partners.
- Way of working
Through close collaboration with your business, we create customized solutions aligned with your specific requirements, resulting in sustainable outcomes.
- Our service portfolio
- About Us
- Who we are
We are a full-service nearshoring provider for digital software products, uniquely positioned as a high-quality partner with native-speaking local experts, perfectly aligned with your business needs.
- Meet our team
ProductDock’s experienced team proficient in modern technologies and tools, boasts 15 years of successful projects, collaborating with prominent companies.
- Our locations
We are ProductDock, a full-service nearshoring provider for digital software products, headquartered in Berlin, with engineering hubs in Lisbon, Novi Sad, Banja Luka, and Doboj.
- Why nearshoring
Elevate your business efficiently with our premium full-service software development services that blend nearshore and local expertise to support you throughout your digital product journey.
- Who we are
- Our work
- Career
- Life at ProductDock
We’re all about fostering teamwork, creativity, and empowerment within our team of over 120 incredibly talented experts in modern technologies.
- Open positions
Do you enjoy working on exciting projects and feel rewarded when those efforts are successful? If so, we’d like you to join our team.
- Candidate info guide
How we choose our crew members? We think of you as a member of our crew. We are happy to share our process with you!
- Life at ProductDock
- Newsroom
- News
Stay engaged with our most recent updates and releases, ensuring you are always up-to-date with the latest developments in the dynamic world of ProductDock.
- Events
Expand your expertise through networking with like-minded individuals and engaging in knowledge-sharing sessions at our upcoming events.
- News
- Blog
- Get in touch
22. Feb 2024 •2 minutes read
Exploring vendor agnostic feature management with OpenFeature
Jovica Zorić
Chief Technology Officer
Feature flags have become a cornerstone of modern software development, empowering developers to modify system behavior without rewriting code. By placing conditional statements, usually if-else cases, around sections of code, developers can control the activation or deactivation of features in real time.
Feature flags are not just tools for developers; they have far-reaching benefits across various business functions. They enable continuous deployment, incremental rollouts, A/B testing, risk mitigation, and feature experimentation. Their versatility extends beyond the coding realm to impact sales, customer support, marketing, and more. While feature flags may appear straightforward to implement and comprehend, it’s important to understand that they introduce additional code and complexity to your existing codebase. If a feature is eventually made accessible to all users, the additional code associated with it should be removed.
In this blog post, we’ll explore the transformative power of feature flags and introduce OpenFeature, a vendor-agnostic solution designed to work with any feature flag management tool or in-house solution.
Alright, let’s kick things off. In the vast sea of tools and software options, we have contenders like LaunchDarkly, Rollout, ConfigCat, Optimizely, Toggled, DevCycle, Unleash, and the list goes on. Personally, I lean towards standards and the freedom to experiment with various providers and solutions until I discover the one that best aligns with my needs. This is where OpenFeature steps in, as it allows you to explore different providers seamlessly.
Use case
Let’s delve into a practical use case. We aim to leverage feature flags to determine if a user, identified by their email address, has access to a new feature. I’ve chosen three providers—flagd, ConfigCat, and DevCycle—but for a comprehensive ecosystem overview, feel free to visit this page.
The example is written in Quarkus and you can find the source code here.
flagd
Let’s create a simple API for our use case.
@Path("/api")
public class PdAPI {
@Inject()
@FlagdQualifier
OpenFeatureAPI openFeatureAPI;
@GET()
@Path("/check/{email}")
@Produces(MediaType.APPLICATION_JSON)
public Response checkFeature(@PathParam("email") String email) {
final var client = openFeatureAPI.getClient();
client.addHooks(new PDTrackerHook());
var ctx = new MutableContext();
ctx.setTargetingKey("isfromproductdock");
ctx.add("Email", email);
if (client.getBooleanValue("isfromproductdock", false, ctx)) {
return Response.ok(new FeatureFlagResponse("This feature is enabled! User is from ProductDock.")).build();
}
return Response.ok(new FeatureFlagResponse("This feature is disabled! User is not from ProductDock.")).build();
}
}
record FeatureFlagResponse(String message) {}
We have a simple API with a GET method that, depending on the email provided, will return a message if the feature is enabled or disabled. To be able to inject and use OpenFeatureAPI, we need to create an instance and set our flagd provider. Don’t mind the Qualifier annotations, we will use different Qualifiers to distinguish between different implementations.
@ApplicationScoped
public class FlagdAPI {
@Produces
@FlagdQualifier
public OpenFeatureAPI getApi() {
final OpenFeatureAPI openFeatureAPI = OpenFeatureAPI.getInstance();
openFeatureAPI.setProvider(new FlagdProvider());
return openFeatureAPI;
}
}
Now that we have our instance in place, we need to run flagd. Visit this link to explore different installation options and their documentation. For this demo, we just run a Docker container:
docker run --rm -it --name flagd -p 8013:8013 -v $(pwd)/flagd-docker:/etc/flagd ghcr.io/open-feature/flagd:latest start --uri file:./etc/flagd/flagd.json
and provide the following config:
{
"flags": {
"isfromproductdock": {
"state": "ENABLED",
"variants": {
"on": true,
"off": false
},
"defaultVariant": "off",
"targeting": {
"if": [
{
"$ref": "isFromProductDock"
},
"on",
null
]
}
}
},
"$evaluators": {
"isFromProductDock": {
"in": [
"@productdock.com",
{
"var": ["Email"]
}
]
}
}
}
It’s easy to understand, and flagd docs are awesome.
After running the application, we can use curl to test our API:
curl http://localhost:8080/api/check/example@productdock.com
or
curl http://localhost:8080/api/check/example@gmail.com
How difficult is it to change to another provider? Let’s try ConfigCat.
ConfigCat
We created a ConfigCat account and an SDK key that we will be using in our configuration.
Here is what the flag looks like in ConfigCat.
Next, we need to configure the OpenFeatureAPI instance to use ConfigCat as the provider.
@ApplicationScoped
public class ConfigCat {
@ConfigProperty(name = "configcat.sdkkey")
public String configCatKey;
@Produces
@ConfigCatQualifier
public OpenFeatureAPI getApi() {
final OpenFeatureAPI openFeatureAPI = OpenFeatureAPI.getInstance();
var configCat = ConfigCatProviderConfig.builder()
.sdkKey(configCatKey)
.build();
openFeatureAPI.setProviderAndWait(new ConfigCatProvider(configCat));
return openFeatureAPI;
}
}
Finally, we change the qualifier in our PdAPI so that our DI container knows which OpenFeatureAPI instance to use.
@Path("/api")
public class PdAPI {
@Inject()
// @FlagdQualifier
@ConfigCatQualifier
OpenFeatureAPI openFeatureAPI;
@GET()
@Path("/check/{email}")
@Produces(MediaType.APPLICATION_JSON)
public Response checkFeature(@PathParam("email") String email) {
final var client = openFeatureAPI.getClient();
client.addHooks(new PDTrackerHook());
var ctx = new MutableContext();
ctx.setTargetingKey("isfromproductdock");
ctx.add("Email", email);
if (client.getBooleanValue("isfromproductdock", false, ctx)) {
return Response.ok(new FeatureFlagResponse("This feature is enabled! User is from ProductDock.")).build();
}
return Response.ok(new FeatureFlagResponse("This feature is disabled! User is not from ProductDock.")).build();
}
}
record FeatureFlagResponse(String message) {
}
Everything else stays the same.
DevCycle
For DevCycle, it’s the same story, almost. They use “email” instead of “Email” for their user object, but that’s an easy fix.
Before we continue, you should have a DevCycle account and your SDK key.
The following image shows the DevCycle UI for defining targeting rules.
Once again, we configure the OpenFeatureAPI instance, but now with the DevCycle provider.
@ApplicationScoped
public class DevCycle {
@ConfigProperty(name = "devcycle.sdkkey")
public String devCycleKey;
@Produces
@DevCycleQualifier
public OpenFeatureAPI getApi() {
final OpenFeatureAPI openFeatureAPI = OpenFeatureAPI.getInstance();
DevCycleLocalOptions options = DevCycleLocalOptions.builder().build();
DevCycleLocalClient devCycleClient = new DevCycleLocalClient(devCycleKey, options);
// This is wild. Should be handled by the client.
// https://github.com/DevCycleHQ/java-server-sdk/pull/111/files/4e21dc9a8f7d5d4d063528b355fc5c6125d9c78b#r1381707824
for (int i = 0; i < 10; i++) {
if (devCycleClient.isInitialized()) {
break;
}
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
openFeatureAPI.setProvider(devCycleClient.getOpenFeatureProvider());
return openFeatureAPI;
}
}
We change the Qualifier and the email field casing for the context.
@Path("/api")
public class PdAPI {
@Inject()
// @FlagdQualifier
// @ConfigCatQualifier
@DevCycleQualifier
OpenFeatureAPI openFeatureAPI;
@GET()
@Path("/check/{email}")
@Produces(MediaType.APPLICATION_JSON)
public Response checkFeature(@PathParam("email") String email) {
final var client = openFeatureAPI.getClient();
client.addHooks(new PDTrackerHook());
var ctx = new MutableContext();
ctx.setTargetingKey("isfromproductdock");
// ctx.add("Email", email);
ctx.add("email", email);
if (client.getBooleanValue("isfromproductdock", false, ctx)) {
return Response.ok(new FeatureFlagResponse("This feature is enabled! User is from ProductDock.")).build();
}
return Response.ok(new FeatureFlagResponse("This feature is disabled! User is not from ProductDock.")).build();
}
}
record FeatureFlagResponse(String message) {
}
Hooks
You might have noticed the addHooks code. I like the idea of tracking, and the hooks are a perfect place to set this up. I haven’t done anything special, just wanted to see how it behaves.
public class PDTrackerHook implements BooleanHook {
private static final Logger LOG = Logger.getLogger(PDTrackerHook.class);
@Override
public void after(HookContext<Boolean> ctx, FlagEvaluationDetails<Boolean> details, Map<String, Object> hints) {
LOG.info(details.getFlagKey() + " : " + details.getValue());
}
}
More on hooks, visit this page.
What’s next
As you can see in the examples above, OpenFeature gives you flexibility with feature management providers. The team behind OpenFeature is actively working on further improvements, and I’m eagerly anticipating the exciting development ahead.
While this article primarily focuses on the server-side implementation, it’s important to note that OpenFeature also has robust client-side capabilities. If you’re keen to dive in, I recommend beginning with exploring their documentation. From there, feel free to explore any direction that aligns with your goals and preferences.
Tags:
Jovica Zorić
Chief Technology OfficerJovica is a techie with more than ten years of experience. His job has evolved throughout the years, leading to his present position as the CTO at ProductDock. He will help you to choose the right technology for your business ideas and focus on the outcomes.