Hands on with flagd: Open source feature flagging
Published: 2024-01-05 03:53:06

[flagd](https://flagd.dev) is a fully open source feature flag system released as part of the OpenFeature project. Essentially, flagd in a fully-featured feature flag evaluation storage and backend evaluation system + an API and / or gRPC endpoint to interact and retrieve flag values.

[flagd](https://flagd.dev) comes various forms: [standalone binaries for every OS](https://github.com/open-feature/flagd/releases
), a [docker image](https://flagd.dev/installation/#docker) or [a Kubernetes Operator](https://github.com/open-feature/open-feature-operator).

## So how does it work?

Let's start with the "standalone" version of flagd. Download the [latest binary for your OS from the releases page](https://github.com/open-feature/flagd/releases/tag/flagd%2Fv0.8.1).

Extract the binary, add the executable bit (`chmod +x flagd_...`) and run the binary. You will see output like this:

```shell
% ./flagd_darwin_arm64 

		 ______   __       ________   _______    ______      
		/_____/\ /_/\     /_______/\ /______/\  /_____/\     
		\::::_\/_\:\ \    \::: _  \ \\::::__\/__\:::_ \ \    
		 \:\/___/\\:\ \    \::(_)  \ \\:\ /____/\\:\ \ \ \   
		  \:::._\/ \:\ \____\:: __  \ \\:\\_  _\/ \:\ \ \ \  
		   \:\ \    \:\/___/\\:.\ \  \ \\:\_\ \ \  \:\/.:| | 
		    \_\/     \_____\/ \__\/\__\/ \_____\/   \____/_/ 		

Flagd is a simple command line tool for fetching and presenting feature flags to services. It is designed to conform to Open Feature schema for flag definitions.

Usage:
  flagd [command]
...
```

## Create your first flag

Let's create your very first flag: a simple `String` flag called `basic-string` which has two possible values: `foo` (which returns `"bar"`) and `bar` (which returns `"other"`). The `foo` variant is set as the `defaultValue` which means `"bar"` should be returned. Save the following as a JSON file:

**flags.json**
```json
{
  "flags": {
    "basic-string": {
      "state": "ENABLED",
      "defaultVariant": "foo",
      "variants": {
        "foo": "bar",
        "bar": "other"
      },
      "targeting": {}
    }
  }
}
```

Now restart flagd and point it to your `flags.json` file:

```
./flagd start --uri file:flags.json
```

You should see:

```
Flag Evaluation listening at [::]:8013	{"component": "service"}
```

## Retrieve a flag value

Open a new terminal window and request the `basic-string` flag:

```shell
curl -X POST "http://localhost:8013/flagd.evaluation.v1.Service/ResolveString" \ 
  -d '{"flagKey":"basic-string","context":{}}' -H "Content-Type: application/json"
```

You should see:

```json
{
  "value":"bar",
  "reason":"STATIC",
  "variant":"foo",
  "metadata":{}
}
```

## Update defaultValue at runtime

flagd updates flag values at runtime, without a restart.

Leave flagd running and update `flags.json` so that `defaultValue` is now `"bar"`.

**flags.json**
```
{
  "flags": {
    "basic-string": {
      "state": "ENABLED",
      "defaultVariant": "bar",
      "variants": {
        "foo": "bar",
        "bar": "other"
      },
      "targeting": {}
    }
  }
}
```

Re-execute the cURL command to retrieve the `basic-string` flag and you should see the `value` is now `"other"`. :

```shell
curl -X POST "http://localhost:8013/flagd.evaluation.v1.Service/ResolveString" \ 
  -d '{"flagKey":"basic-string","context":{}}' -H "Content-Type: application/json"
```

```
{
  "value":"other",
  "reason":"STATIC",
  "variant":"bar",
  "metadata":{}
}
```

## What about Kubernetes?

flagd works on Kubernetes too:

[Install cert-manager](https://cert-manager.io/docs/installation/helm/) if you don't have it already

Install the [OpenFeature Operator](https://flagd.dev/reference/openfeature-operator/installation/)

```
helm repo add openfeature https://open-feature.github.io/open-feature-operator/
helm repo update
helm upgrade --install openfeature openfeature/open-feature-operator
```

Create the feature flag via a `FeatureFlag` CRD:

```
kubectl apply -f - <<EOF
apiVersion: core.openfeature.dev/v1beta1
kind: FeatureFlag
metadata:
  name: sample-flags
spec:
  featureFlagSpec:
    flags:
      "basic-string":
        state: "ENABLED"
        variants:
          "foo": "bar"
          "bar": "other"
        defaultVariant: "foo"
        targeting: {}
EOF
```

Tell the operator **where** to find the flags by defining a `FeatureFlagSource`:

```
kubectl apply -f - <<EOF
apiVersion: core.openfeature.dev/v1beta1
kind: FeatureFlagSource
metadata:
  name: feature-flag-source
spec:
  sources:
  - source: default/sample-flags
    provider: kubernetes
  port: 8080
EOF
```

Finally, enable those flags for your deployment by using the `openfeature.dev/enabled` and `openfeature.dev/featureflagsource` annotations:
```
apiVersion: apps/v1
kind: Deployment
metadata:
  name: your-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-busybox-curl-app
  template:
    metadata:
      labels:
        app: my-busybox-curl-app
      annotations:
        # here are the annotations for OpenFeature Operator
        openfeature.dev/enabled: "true"
        openfeature.dev/featureflagsource: "default/feature-flag-source"
    spec:
      containers:
        - name: busybox
          image: yauritux/busybox-curl:latest
          ports:
            - containerPort: 80
          args:
            - sleep
            - "30000"
```

From within your application (ie. the above pod), the flagd endpoints are available at `localhost:8080` so you can do:

```
// From within the pod
curl --location 'http://localhost:8080/flagd.evaluation.v1.Service/ResolveString' --header 'Content-Type: application/json' --data '{ "flagKey":"basic-string"}'
```

In a real application, rather than curl, you would probably use the OpenFeature SDK with the flagd provider.

## That's all great, but how do I build that flag JSON?

As you've seen, flagd is very simple. Arguably the hardest part is defining the flags themselves. Guessing the JSON is no fun! Thankfully, the [flagd playground](https://flagd.dev/playground/) provides demo code and [a live testing playground](https://flagd.dev/playground/) which makes the JSON easy!