|
| 1 | +--- |
| 2 | +name: feature-flags |
| 3 | +description: >- |
| 4 | + Create, modify, and remove feature flags in RedisInsight. Use when adding a |
| 5 | + new feature flag, introducing a dev flag, promoting a dev flag to regular, |
| 6 | + cleaning up old flags, or the user mentions feature flags, feature toggles, |
| 7 | + or gating features. |
| 8 | +--- |
| 9 | + |
| 10 | +# Feature Flags |
| 11 | + |
| 12 | +RedisInsight has its own feature flag system. Flags are defined in a remote JSON config, fetched by the backend, and served to the frontend via API. This skill covers how to add, promote, and remove flags. |
| 13 | + |
| 14 | +## Flag Types |
| 15 | + |
| 16 | +| Type | Naming | `flag` value | Strategy | Purpose | |
| 17 | +| ---------------------------- | --------------------------------- | ------------ | ------------------------ | ------------------------------------------------------------ | |
| 18 | +| **Dev flag** | `dev-<name>` (e.g. `dev-browser`) | `false` | `CommonFlagStrategy` | Hide incomplete features during development | |
| 19 | +| **Regular flag** | `camelCase` (e.g. `azureEntraId`) | `true` | `CommonFlagStrategy` | Standard on/off toggle | |
| 20 | +| **Regular with data** | `camelCase` | `true` | `WithDataFlagStrategy` | Flag + extra config payload in `data` | |
| 21 | +| **Switchable (overridable)** | `camelCase` | `true` | `SwitchableFlagStrategy` | User can override locally via `~/.redis-insight/config.json` | |
| 22 | + |
| 23 | +## Files to Change |
| 24 | + |
| 25 | +Every new flag touches these files (in order): |
| 26 | + |
| 27 | +### Backend (required) |
| 28 | + |
| 29 | +1. **`redisinsight/api/config/features-config.json`** |
| 30 | + Add the flag entry with `flag`, `perc`, optional `filters` and `data`. Bump the `version` number. |
| 31 | + |
| 32 | +2. **`redisinsight/api/src/modules/feature/constants/index.ts`** |
| 33 | + Add to the `KnownFeatures` enum. |
| 34 | + |
| 35 | +3. **`redisinsight/api/src/modules/feature/constants/known-features.ts`** |
| 36 | + Add entry to the `knownFeatures` record with `name` and `storage` (usually `FeatureStorage.Database`). |
| 37 | + |
| 38 | +4. **`redisinsight/api/src/modules/feature/providers/feature-flag/feature-flag.provider.ts`** |
| 39 | + Register the flag with its strategy (see Strategy Types below). |
| 40 | + |
| 41 | +### Frontend (required if the flag gates UI) |
| 42 | + |
| 43 | +5. **`redisinsight/ui/src/constants/featureFlags.ts`** |
| 44 | + Add to the `FeatureFlags` enum. |
| 45 | + |
| 46 | +6. **`redisinsight/ui/src/slices/app/features.ts`** |
| 47 | + Add default state entry in `initialState.featureFlags.features` with `{ flag: false }`. |
| 48 | + |
| 49 | +### Consuming code |
| 50 | + |
| 51 | +7. Use the flag in components/hooks to gate functionality. |
| 52 | + |
| 53 | +## Strategy Selection |
| 54 | + |
| 55 | +Choose the strategy based on what the flag needs: |
| 56 | + |
| 57 | +``` |
| 58 | +CommonFlagStrategy → Most flags (dev and regular on/off) |
| 59 | +WithDataFlagStrategy → Flag needs to carry extra data payload |
| 60 | +SwitchableFlagStrategy → Flag should be overridable via local config.json |
| 61 | +``` |
| 62 | + |
| 63 | +Register in `feature-flag.provider.ts`: |
| 64 | + |
| 65 | +```typescript |
| 66 | +this.strategies.set( |
| 67 | + KnownFeatures.YourFeature, |
| 68 | + new CommonFlagStrategy(this.featuresConfigService, this.settingsService), |
| 69 | +); |
| 70 | +``` |
| 71 | + |
| 72 | +## Config JSON Structure |
| 73 | + |
| 74 | +### Minimal (dev flag) |
| 75 | + |
| 76 | +```json |
| 77 | +"dev-myFeature": { |
| 78 | + "flag": false, |
| 79 | + "perc": [[0, 100]] |
| 80 | +} |
| 81 | +``` |
| 82 | + |
| 83 | +### With filters (Electron-only) |
| 84 | + |
| 85 | +```json |
| 86 | +"myFeature": { |
| 87 | + "flag": true, |
| 88 | + "perc": [[0, 100]], |
| 89 | + "filters": [ |
| 90 | + { "name": "config.server.buildType", "value": "ELECTRON", "cond": "eq" } |
| 91 | + ] |
| 92 | +} |
| 93 | +``` |
| 94 | + |
| 95 | +### Gradual rollout (10% of users) |
| 96 | + |
| 97 | +```json |
| 98 | +"myFeature": { |
| 99 | + "flag": true, |
| 100 | + "perc": [[0, 10]] |
| 101 | +} |
| 102 | +``` |
| 103 | + |
| 104 | +### With data payload |
| 105 | + |
| 106 | +```json |
| 107 | +"myFeature": { |
| 108 | + "flag": true, |
| 109 | + "perc": [[0, 100]], |
| 110 | + "data": { "strategy": "ioredis" } |
| 111 | +} |
| 112 | +``` |
| 113 | + |
| 114 | +## Filter Conditions |
| 115 | + |
| 116 | +Filters compare a value from server state against the filter value. |
| 117 | + |
| 118 | +| Condition | Meaning | |
| 119 | +| ------------ | ------------------------------- | |
| 120 | +| `eq` | equals | |
| 121 | +| `neq` | not equals | |
| 122 | +| `gt` / `gte` | greater than / greater or equal | |
| 123 | +| `lt` / `lte` | less than / less or equal | |
| 124 | + |
| 125 | +Common `name` paths: `config.server.buildType` (ELECTRON, DOCKER_ON_PREMISE, REDIS_STACK), `config.server.packageVersion` (uses semver), `agreements.analytics`, `env.<VAR_NAME>`. |
| 126 | + |
| 127 | +Filters support `and`/`or` composition for complex conditions. |
| 128 | + |
| 129 | +## Workflows |
| 130 | + |
| 131 | +### Add a dev feature flag |
| 132 | + |
| 133 | +Use for features under active development that should not be visible in production. |
| 134 | + |
| 135 | +1. `features-config.json` → add `"dev-myFeature": { "flag": false, "perc": [[0, 100]] }` |
| 136 | +2. `constants/index.ts` → add `DevMyFeature = 'dev-myFeature'` to `KnownFeatures` |
| 137 | +3. `constants/known-features.ts` → add record entry |
| 138 | +4. `feature-flag.provider.ts` → register with `CommonFlagStrategy` |
| 139 | +5. `ui/src/constants/featureFlags.ts` → add `devMyFeature = 'dev-myFeature'` |
| 140 | +6. `ui/src/slices/app/features.ts` → add default `{ flag: false }` |
| 141 | + |
| 142 | +### Promote dev flag to regular flag |
| 143 | + |
| 144 | +When the feature is complete and ready for rollout. |
| 145 | + |
| 146 | +1. Rename `dev-myFeature` → `myFeature` in all the files above |
| 147 | +2. Set `flag: true` in `features-config.json` |
| 148 | +3. Optionally set `perc` for gradual rollout (e.g. `[[0, 10]]`) |
| 149 | +4. Change strategy if needed (e.g. to `SwitchableFlagStrategy` for overridable) |
| 150 | +5. Bump config `version` |
| 151 | + |
| 152 | +### Clean up a flag |
| 153 | + |
| 154 | +When a feature is fully rolled out and the flag is no longer needed. |
| 155 | + |
| 156 | +1. Remove from `features-config.json` |
| 157 | +2. Remove from `KnownFeatures` enum |
| 158 | +3. Remove from `knownFeatures` record |
| 159 | +4. Remove strategy registration from `feature-flag.provider.ts` |
| 160 | +5. Remove from FE `FeatureFlags` enum |
| 161 | +6. Remove default state from `features.ts` |
| 162 | +7. Remove all gating code (conditionals, `FeatureFlagComponent` wrappers) in consuming components |
| 163 | + |
| 164 | +## FE Usage Patterns |
| 165 | + |
| 166 | +### Check flag in a component |
| 167 | + |
| 168 | +```typescript |
| 169 | +import { FeatureFlags } from 'uiSrc/constants'; |
| 170 | +import { appFeatureFlagsFeaturesSelector } from 'uiSrc/slices/app/features'; |
| 171 | + |
| 172 | +const features = useSelector(appFeatureFlagsFeaturesSelector); |
| 173 | +const isEnabled = features[FeatureFlags.myFeature]?.flag; |
| 174 | +``` |
| 175 | + |
| 176 | +### Custom selector for complex logic |
| 177 | + |
| 178 | +```typescript |
| 179 | +export const isMyFeatureEnabledSelector = (state: RootState): boolean => { |
| 180 | + const features = state.app.features.featureFlags.features; |
| 181 | + return features[FeatureFlags.myFeature]?.flag ?? false; |
| 182 | +}; |
| 183 | +``` |
0 commit comments