Service Mesh vs. OpenFeature: The Showdown
Service meshes and OpenFeature can both handle canary and progressive rollouts. Both let you reduce risk when shipping change. The approaches differ a lot, though, and since we keep getting asked about this, it's time to tackle it.
We want to be honest here. Our goal is to show the different approaches and where each one outshines the other.
The fundamental difference
Service meshes handle routing at the infrastructure level, outside your application. OpenFeature lives inside your application, as part of your code.
That changes what you ship:
- With a service mesh, you run two artifacts in parallel (blue/green, canary, versioned deployments). For each request, the mesh decides which one serves it.
- With OpenFeature, all code paths live in the same artifact. The application decides at runtime which one to take.
Behind this sits an assumption. Traffic shifting between two artifacts implicitly asserts that both behave the same, or close to it. You're checking that the new version is safe, not choosing who gets which behavior. When you know the behavior differs, pure traffic shifting gives you much less control. You can't exempt specific users, offer opt-in, or target by anything beyond what's visible at the request level.
Where service meshes win
Identical-behavior changes. A pure refactor, a dependency upgrade, a low-level setting like a GC algorithm: there's no reason to bucket users, you just want a binary change to land safely. A mesh is ideal here, and it keeps flag logic out of your application.
Separation of concerns. Different versions live in different artifacts, so accidental side effects are heavily limited. The only overhead is the routing.
Ownership and blast radius. Platform teams can shift or revert traffic without a deployment, and without depending on how each team handles its flags.
Consistency across languages. The proxy behaves the same no matter what runs behind it.
Where it gets expensive
The mesh model works well with one or two variants. It stops scaling when you want several at once, such as internal dogfooding, a migration, and premium features:
- Each artifact needs its own build and a way to be separated in code (branches, build flags, etc.).
- Every variation is a configuration change in the platform layer, which not every developer has access to or is familiar with.
- The combinations multiply. Ten boolean flags mean 1,024 permutations. Nobody is going to build 1,024 artifacts and shift traffic between them.
Where OpenFeature shines
Anything where behavior is meant to differ. A new code path you want full control over: who sees it, who opts in, who is excluded, and when it expands.
Integrate early.
Long-living implementations used to mean long-living branches.
With flags, changes land on main early behind a switch, and you can start experimenting sooner without touching service configuration.
Rich evaluation context.
Say you want to experiment with teenage users in Germany on Android devices.
With a mesh, that data has to be available at the request level, which usually means building and running something like an ext_proc or ext_authz service in the hot path.
In the application, it's a call to a third-party service, made on demand when the flag is evaluated rather than on every route call.
Observability. OpenTelemetry has semantic conventions for feature flags, so the variant a user received shows up directly on the span where the decision happened. That makes experiments much easier to analyze than correlating routing config with traffic after the fact.
The costs of OpenFeature
Flag debt. Unremoved flags turn into invisible code branches. Routing configuration is often easier to see and audit, so flags need discipline: owners, expiry dates, and regular cleanup.
The SDK problem. The strongest argument against application-level flagging is that every SDK needs to support the same feature set, behave consistently, and be used properly everywhere. The mesh is centralized at the infrastructure level, so it has less complexity here.
That's a fair point, and it's why standardization is such a focus for OpenFeature:
- One API across languages. Evaluating a flag looks and behaves the same in Java, Go, JavaScript, or Python.
- Consistent semantics. Evaluation context, default values, error handling, and hooks follow a shared specification rather than each vendor's interpretation.
- Providers instead of lock-in. The flag backend is swappable behind the API, so SDK behavior doesn't depend on which vendor you choose.
There's also a question of pace. Organizations that run many experiments and releases in parallel can't route each one through the infrastructure layer. Dynamic, in-application control is how they keep up. Not every team is ready for that model yet, and that's fine, but it's where the practice is heading.
Which one should I use?
| Situation | Reach for |
|---|---|
| Refactor, dependency upgrade, or low-level setting where behavior should be identical | Service mesh |
| Shipping a new version of a service | Service mesh |
| Platform team needs to shift or revert traffic without a deploy | Service mesh |
| New code path where you need control over who gets it | OpenFeature |
| Targeting by user attributes or third-party data | OpenFeature |
| Opt-in, exemptions, or gradual expansion by cohort | OpenFeature |
| Experiments you want to analyze via traces | OpenFeature |
| Several variants at once (dogfooding, migration, premium) | OpenFeature |
A rule of thumb: if you're asserting that nothing changes for users, use traffic shifting. If you're not, use a feature flag.
Better together
This isn't an either/or decision. Use the mesh for traffic shifting at the deployment level, and flags for experiments and business-level targeting. Many teams use both: the mesh to roll out the artifact, and OpenFeature to roll out the features inside it.
The interesting part is where the two meet. Could the mesh carry the flag decision or targeting information with the request, for example in a header or as OpenTelemetry baggage, so both layers agree on which variant a user receives? That's an open question we'd like to explore with the community. If you're running both today, we'd love to hear how you connect them in the #openfeature channel on the CNCF Slack.
