|
| 1 | +# Feature flags |
| 2 | + |
| 3 | +Feature flags are implemented using [OpenFeature](https://openfeature.dev/) with an `InMemoryProvider`. Flags are declared in YAML files and loaded at startup. |
| 4 | + |
| 5 | +## Flag files |
| 6 | + |
| 7 | +Each environment has its own flag file. The application selects the file based on the `DEPLOYED_TO` environment variable, falling back to `flags.yml` for local development where `DEPLOYED_TO` is not set. |
| 8 | + |
| 9 | +| Environment | File | |
| 10 | +| ---------------------------- | ---------------------- | |
| 11 | +| dev | `flags.dev.yml` | |
| 12 | +| review | `flags.review.yml` | |
| 13 | +| preprod | `flags.preprod.yml` | |
| 14 | +| production | `flags.production.yml` | |
| 15 | +| local (no `DEPLOYED_TO` set) | `flags.yml` | |
| 16 | + |
| 17 | +## Adding a flag |
| 18 | + |
| 19 | +**1. Declare the flag in each environment's file.** |
| 20 | + |
| 21 | +Add an entry to all five flag files (`flags.yml`, `flags.dev.yml`, `flags.review.yml`, `flags.preprod.yml`, `flags.production.yml`). Set the value to `true` to enable or `false` to disable in that environment. |
| 22 | + |
| 23 | +```yaml |
| 24 | +flags: |
| 25 | + my_flag: false |
| 26 | +``` |
| 27 | +
|
| 28 | +**2. Check the flag in application code.** |
| 29 | +
|
| 30 | +```python |
| 31 | +from openfeature import api |
| 32 | + |
| 33 | +client = api.get_client() |
| 34 | +if client.get_boolean_value("my_flag", False): |
| 35 | + # feature is enabled |
| 36 | +``` |
| 37 | + |
| 38 | +The second argument to `get_boolean_value` is the fallback returned if the flag is missing or the provider is unavailable. If a flag name is not found in the YAML file, OpenFeature silently returns the fallback rather than raising an error — so a typo in a flag name will return `False` without any indication something is wrong. |
| 39 | + |
| 40 | +## Enabling a flag in tests |
| 41 | + |
| 42 | +Use the `with_flag_enabled` fixture to turn a flag on for the duration of a single test: |
| 43 | + |
| 44 | +```python |
| 45 | +def test_something(with_flag_enabled): |
| 46 | + with_flag_enabled("my_flag") |
| 47 | + # flag is enabled for this test only |
| 48 | +``` |
| 49 | + |
| 50 | +Multiple flags can be enabled by calling it more than once: |
| 51 | + |
| 52 | +```python |
| 53 | +def test_something(with_flag_enabled): |
| 54 | + with_flag_enabled("my_flag") |
| 55 | + with_flag_enabled("another_flag") |
| 56 | +``` |
| 57 | + |
| 58 | +In class-based tests the fixture must be relayed via an `autouse` fixture so it is accessible to helper methods: |
| 59 | + |
| 60 | +```python |
| 61 | +@pytest.fixture(autouse=True) |
| 62 | +def flags(self, with_flag_enabled): |
| 63 | + self.with_flag_enabled = with_flag_enabled |
| 64 | + |
| 65 | +def and_my_flag_is_enabled(self): |
| 66 | + self.with_flag_enabled("my_flag") |
| 67 | +``` |
| 68 | + |
| 69 | +After each test the flags are reset to the defaults from the YAML file. |
0 commit comments