ZIO OpenFeature

A ZIO-native wrapper around the OpenFeature Java SDK for Scala 2.13 and Scala 3.

Get Started View on GitHub

Maven Central


Why ZIO OpenFeature?

ZIO OpenFeature wraps the OpenFeature Java SDK, giving you access to the entire OpenFeature ecosystem while providing a type-safe, functional API designed for ZIO applications.

  • Use Any Provider - Works with all OpenFeature providers: LaunchDarkly, Flagsmith, Flipt, flagd, and more
  • Type Safety - Compile-time guarantees with the FlagType type class
  • ZIO Native - Effect-based API with proper resource management
  • Transactions - Scoped flag overrides with caching and evaluation tracking
  • Testkit - In-memory provider for testing without external dependencies

Quick Example

import zio.*
import zio.openfeature.*
import dev.openfeature.contrib.providers.flagd.FlagdProvider

object MyApp extends ZIOAppDefault:

  val program = for
    enabled <- FeatureFlags.boolean("new-feature", default = false)
    _       <- ZIO.when(enabled)(Console.printLine("Feature enabled!"))
  yield ()

  def run = program.provide(
    Scope.default >>> FeatureFlags.fromProvider(new FlagdProvider())
  )

Two ways to read a flag

The call above names the key, the type and the default at the use site. That is the direct style, and it is fully supported. If the same flag is read from more than one place, declaring it once is usually the simpler option — the key, type and default stop being repeated, and a second call site can no longer disagree about them:

object Flags:
  val NewFeature = FlagDef("new-feature", false, "the new checkout flow")

// anywhere
FeatureFlags.value(Flags.NewFeature)           // ZIO[FeatureFlags, FeatureFlagError, Boolean]
FeatureFlags.valueOrDefault(Flags.NewFeature)  // never fails — serves the definition's default

Both styles run through the same evaluation path — hooks, transactions, caching and error semantics are identical, and you can mix them in one codebase. See Typed Flags.

Documentation

Start with Getting Started, then Typed Flags if you want the declare-once style. The rest are reference pages — read them when you reach the problem they solve.

Section Description
Getting Started Installation and basic usage
Typed Flags Declare a flag once — key, type and default — and evaluate it by name
Architecture Core design and components
Providers Using OpenFeature providers
Extras HOCON, env var, and caching providers
Optimizely Optimizely Feature Experimentation integration
Evaluation Context Targeting and context hierarchy
Hooks Cross-cutting concerns
Transactions Flag overrides and tracking
Testkit Testing utilities
Testing Real Providers Fault-testing a real provider over TLS-MITM
Spec Compliance OpenFeature specification compliance

Added in 1.1.0

Everything below is documented in the pages above; this table is a shortcut to the right section.

Feature What it gives you Where
Typed tier fails on error codes value / *Details fail with a typed FlagNotFound, TypeMismatch, … when the provider reports an error code — a fail-closed gate stays closed; the total tier still serves the default Getting Started
NestedPolicy.Reuse An inner transaction runs inside the enclosing one instead of failing — a per-request transaction is safe as middleware Transactions
transactionEvaluations None outside a transaction, Some(...) inside — an audit read can no longer mistake “no transaction” for “nothing evaluated” Transactions
Wrappers take any FeatureProvider CircuitBreakerProvider and CachingProvider no longer require an EventProvider Extras
FlagDef[A] Declare a flag once and evaluate it by name Typed Flags
derives FlagType Codecs for your own enums and case classes, no boilerplate Typed Flags
FlagType.wireType A domain type carried over the wire as a scalar resolves through that scalar’s method, and hooks see it Architecture
Native 64-bit Long long/longDetails resolve the full Long range exactly, instead of losing precision past 2^53 Extras
ContextSource Pull ambient context (MDC, tracing, correlation id) into every evaluation Evaluation Context
FallbackLogging A served default leaves a warn line, rate-limited per flag key Getting Started
verify + AcquireStatus Reject a real provider that constructed but cannot serve, and ask whether it is live yet Providers
FLAG_NOT_FOUND for absent keys A provider that does not hold a key lets a MultiProvider chain advance Extras, Testkit
Typed fixtures (:=) Pin a test fixture through the flag’s own codec Testkit
TestFeatureProvider.makeNamed Distinct metadata names, so a chain of test providers is really a chain Testkit

Modules

Module Description
core ZIO wrapper around OpenFeature SDK
extras Built-in HOCON, env var, and caching providers
ofrep OpenFeature Remote Evaluation Protocol (OFREP) provider over HTTP
optimizely First-party Optimizely Feature Experimentation integration
testkit In-memory provider for testing

Requirements

  • Scala 2.13+ or Scala 3.3+
  • ZIO 2.1+

License

ZIO OpenFeature is distributed under the Apache 2.0 License.


Copyright © 2026 Mohsen Zainalpour. Distributed under the Apache 2.0 license.

This site uses Just the Docs, a documentation theme for Jekyll.