What are References in Flipt?

References are a way to pass additional information to Flipt during an evaluation request. This allows you to serve different flag states based on the reference when using our Git backend.

References are especially useful when using Flipt in preview environments. You can use references to serve a different Git branch for each preview environment keeping your main branch safe from untested configurations.

What are Preview Environments?

Preview environments are a way to create a temporary environment for a pull request. This allows you to test your changes in a production-like environment before merging your code. This is especially useful for testing changes that require a full build and deploy cycle.

In this guide, we’re going to demonstrate how to leverage Flipt in preview environments to test changes in a production-like environment, without affecting your production users.

What You’ll Learn

In this guide you will learn how to:

  • 🏁 Setup Flipt to work in a preview environment
  • 🚀 Create a preview environment for a pull request that modifies a feature flag in our declarative format
  • 🌲 Add, commit, and push the change to a preview branch
  • 🧪 Test the change in the preview environment
  • 🎉 Merge the pull request and deploy the change to production

Our Example Application

We’re going to be making a change to our internal organization sales dashboard. This dashboard is made up of simple React frontend and a Go backend. The frontend is a single-page application that makes API calls to the backend to fetch data. The backend is a new API that returns a list of sales performance data. The frontend will use this data to render a graph of our company’s sales performance.

Because this API is new and we’re not sure how it will perform, we want to test it in a production-like environment before we merge it into our main branch.

Our example application without our new graph

We already use Flipt in our production environment, and we want to use the same instance without having to deploy a new Flipt specifically for our preview environments.

We also make use of Flipt’s GitOps integration to manage our feature flags in our Git repository. This allows us to manage our feature flags in a declarative format, and have them automatically synced to Flipt in the background.

If you’re not familiar with Flipt’s GitOps integration, check out our GitOps guide for more information.

Structure

Our application lives in a directory committed to a Git repository. For the example’s sake, we assume the repository will be hosted on GitHub at https://github.com/organization/repository.git.

The application is made up of three directories:

  • cmd/api - contains our Go backend API and loads the UI
  • ui - contains our React frontend
  • pkg/performance - contains our Flipt feature flag definition

If you want to follow along you can fork our guide repository.

For this guide, we’ll mainly focus on the Go backend and the Flipt feature flag definition file.

Go Backend

The purpose of our Go backend is two-fold:

  1. Serve our React frontend
  2. Serve our new API

Our new API is a simple HTTP endpoint that returns a list of sales performance data.

Because we’re good engineers, we want to make sure that our new API is performant before we merge it into our main branch. To do this, we’re going to use Flipt to control access to our new API.

The main bit of code that we want to guard is where we mount the /api/performance endpoint:

cmd/api/main.go
	http.HandleFunc("/api/performance", func(w http.ResponseWriter, r *http.Request) {
		logger := slog.With(
			slog.String("namespace", "performance"),
			slog.String("flag", "showPerformanceHistory"),
		)

		// evaluate the showPerformanceHistory features flag
		result, err := flipt.Evaluation().Boolean(r.Context(), &evaluation.EvaluationRequest{
			NamespaceKey: "performance",
			FlagKey:      "showPerformanceHistory",
			EntityId:     fmt.Sprintf("%x", rand.Intn(1000)),
			Reference:    os.Getenv("FLIPT_CLIENT_REFERENCE"),
		})
		if err != nil {
			logger.Error("evaluating flag", "error", err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		// if the flag is disabled we return that the endpoint cannot be found
		if !result.Enabled {
			logger.Debug("flag disabled")
			http.Error(w, "path not found", http.StatusNotFound)
			return
		}

		if err := json.NewEncoder(w).Encode(&history); err != nil {
			logger.Error("parsing json", "error", err)
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	})

Here you can see that we’re using Flipt’s Go client to evaluate the showPerformanceHistory feature flag. If the flag is enabled, we return the sales performance data.

Also of note, we’re using the FLIPT_CLIENT_REFERENCE environment variable to pass in the reference to Flipt in the evaluation call. This is used to serve a different Git branch for each preview environment. We’ll talk more about this later.

Flipt Feature Flag Definition

Our feature flag definition is stored in the pkg/performance directory. This directory contains a single file called features.yml. This file contains the definition of our showPerformanceHistory feature flag.

pkg/performance/features.yml
namespace: performance
flags:
  - key: showPerformanceHistory
    name: Show Performance History Graph
    type: BOOLEAN_FLAG_TYPE
    enabled: false

You don’t have to call your file features.yml and you can spread your flag definitions across multiple files. Checkout our docs on locating flag state to learn more.

Creating Preview Environments

For this guide, we’re going to use GitHub Actions to deploy our preview environments to Koyeb.

Koyeb has a nice tutorial on how to deploy a preview environment using GitHub Actions. You can find it here.

To get our preview environments working with Flipt, we’ll need some way of passing the Git branch name to Flipt. We can do this by setting the FLIPT_CLIENT_REFERENCE environment variable to the Git branch name. This will allow us to serve a different Git branch for each preview environment.

To do this, we’ll need to add a step to our GitHub Actions workflow that sets the FLIPT_CLIENT_REFERENCE environment variable to the Git branch name.

- name: Deploy the application to Koyeb
  uses: koyeb/action-git-deploy@v1
  with:
    app-name: "dashboard-app-preview-${{ github.head_ref }}"
    service-name: ${{ github.head_ref }}
    service-ports: "8081:http"
    service-routes: "/:8081"
    service-env: "FLIPT_CLIENT_REFERENCE=${{ github.head_ref }},FLIPT_ADDRESS=${{ secrets.FLIPT_ADDRESS }}"
    docker: ghcr.io/flipt-io/dashboard-app:latest

Here we’re also setting the FLIPT_ADDRESS environment variable to the address of our Flipt instance. This could also be configured in your application via a config file.

Enabling the Flag and Pushing to a Preview Branch

Now that we have our preview environments setup, we can enable our feature flag and push our changes to a preview branch.

To enable our feature flag, we’ll need to update our features.yml file to set the enabled field to true.

pkg/performance/features.yml
namespace: performance
flags:
  - key: showPerformanceHistory
    name: Show Performance History Graph
    type: BOOLEAN_FLAG_TYPE
    enabled: true

Now we can add, commit, and push our changes to a preview branch.

git checkout -b feature/enable-performance-history
git add pkg/performance/features.yml
git commit -m "Enable performance history"
git push origin feature/enable-performance-history

We’ll also need to create a pull request for our preview branch. This will trigger our GitHub Actions workflow and deploy our preview environment.

Testing the Change

Before checking on our preview environment, let’s take a look at our Flipt instance. We can see that our feature flag is still showing as disabled in the UI.

This is because Flipt is configured to track the main branch of our Git repository by default, which is what it continues to show in the UI.

Our feature flag is still disabled in the UI

In an upcoming release, we’ll be adding the ability to switch between references in the UI. This will allow you to see the state of your feature flags in each Git branch.

Now that our preview environment is deployed, we can test our new API. We can view our application deployed to Koyeb from the Koyeb dashboard.

You can also view the application settings and see that the FLIPT_CLIENT_REFERENCE environment variable is set to the Git branch name.

Our application settings in Koyeb

Now we can click on the application URL to view our application. We can see that our new API is working and our graph is being rendered. 🎉

This means our feature flag is enabled successfully in our preview environment.

Our example application with our new graph

Merging the Pull Request

Now that we’ve tested our new API in our preview environment, we can merge our pull request and deploy our changed flag definition to production.

Note that we don’t have to make any code changes in our production application. This is because our Flipt is already configured to track the HEAD of the main Git branch. This means that when we merge our pull request, our application will automatically start using the new flag state.

Our feature flag is now enabled in the UI

Recap

In this guide, we learned how to use Flipt’s GitOps integration and references to test changes in a production-like environment without affecting our production users.

Using references allowed us to serve a different Git branch for each preview environment. This means that each of our developers can test their feature flag changes in a production-like environment without affecting other developers.

There are likely many other ways to use references with Flipt. We’d love to hear how you’re using references in your organization.

Further Considerations

References are available for all of our GET API endpoints, (e.g. GET /api/v1/flags) as well as all of our evaluation endpoints (e.g. POST /evaluate/v1/boolean).

We’ve also added support for references in all of our server side REST SDKs and our client side SDKs.

References currently only work with Git, and our git backend, like all of the declarative storage backends, mandates that the UI is read-only. We have thoughts on how this could change in the future, but for now, this is a limitation. You always have your editor, Git and the SCMs (GitHub, Gitlab etc) for state management in the meantime.

Flipt will automatically sync your feature flag definitions to Flipt in the background. Each of these backends work by polling their sources (git, oci, local directory or object store) and the interval can be configured. Checkout the Configuration: Storage: Declarative for details on adjusting these intervals.