Eppo is a modular flagging and experimentation analysis tool. Before proceeding you'll need an Eppo account.

Features

  • Feature gates
  • Kill switches
  • Progressive rollouts
  • A/B/n experiments
  • Mutually exclusive experiments (Layers)
  • Holdouts
  • Contextual multi-armed bandits
  • Dynamic configuration

Installation

Add eppo_sdk to your list of dependencies in mix.exs:

def deps do
  [
    {:eppo_sdk, "~> 0.1.0"}
  ]
end

Usage

Setup

Add the Eppo Client Server to your application's supervision tree:

# In your application.ex
defmodule YourApp.Application do
  use Application

  def start(_type, _args) do
    config = %EppoSdk.Client.Config{
      api_key: System.get_env("EPPO_API_KEY"),
      assignment_logger: YourApp.AssignmentLogger,
      # ... other config options ...
    }

    children = [
      # ... other children ...
      {EppoSdk.Server, config}
    ]

    opts = [strategy: :one_for_one, name: YourApp.Supervisor]
    Supervisor.start_link(children, opts)
  end
end

Implementing an Assignment Logger

The assignment logger is used to track experiment assignments for analytics. Implement the EppoSdk.AssignmentLogger behaviour in your application:

defmodule YourApp.AssignmentLogger do
  @behaviour EppoSdk.AssignmentLogger

  @impl true
  def log_assignment(event) do
    # Implement your logging logic here
    IO.inspect(event, label: "log_assignment")
  end
end

Using the Client

Once the server is started, you can access the client instance anywhere in your application:

# Get the client instance
client = EppoSdk.Server.get_instance()

# Use the client to evaluate feature flags
assignment = EppoSdk.Client.get_string_assignment(
  client,
  "flag-key",
  "user-123",
  %{},
  "default"
)

Manual Start (e.g., for testing)

If you need to start the server manually (not recommended for production):

config = %EppoSdk.Client.Config{api_key: "your-api-key", assignment_logger: YourApp.AssignmentLogger}
{:ok, _pid} = EppoSdk.Server.start_link(config)

# When testing locally, wait for the client to initialize
client = EppoSdk.Server.get_instance()
EppoSdk.Client.wait_for_initialization(client)

# Then use as normal
client = EppoSdk.Server.get_instance()

Or you can use the client directly:

{:ok, client} = EppoSdk.Client.new(config)

# When testing locally, wait for the client to initialize
EppoSdk.Client.wait_for_initialization(client)

Local Development with eppo_core

When developing the Elixir SDK alongside eppo_core, you can configure Cargo to use the local eppo_core package instead of the published version:

  1. Copy the template configuration file:

    cp .cargo/config.toml.template .cargo/config.toml
    
  2. This will configure Cargo to use the local eppo_core package located at ../eppo_core relative to the elixir-sdk directory.

  3. Compile the SDK to use your local eppo_core version:

    mix compile
    
  4. Any changes to the local eppo_core package will now be reflected when you recompile the Elixir SDK.

For publishing releases, make sure to:

  1. Test with the published eppo_core version (remove the .cargo/config.toml)
  2. Ensure the version in native/sdk_core/Cargo.toml matches the published eppo_core version

Testing

To run the tests:

mix test