Skip to content

Commit c20e59b

Browse files
committed
docs: add docs for selector, metadata
Signed-off-by: Todd Baert <[email protected]>
1 parent 44edcc9 commit c20e59b

File tree

6 files changed

+795
-5
lines changed

6 files changed

+795
-5
lines changed

docs/concepts/selectors.md

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
# Selectors
2+
3+
Selectors are query expressions that allow you to filter flag configurations from flagd's sync service. They enable providers to request only specific subsets of flags instead of receiving all flags, making flagd more efficient and flexible for complex deployments.
4+
5+
## Overview
6+
7+
In flagd, **selectors** provide a way to query flags based on different criteria. This is particularly powerful because flagd decouples **flag sources** from **flag sets**, allowing for more granular control over which flags are synchronized and evaluated.
8+
9+
### Key Concepts
10+
11+
- **Flag Source**: Where flag configuration data comes from (file, HTTP endpoint, gRPC service, etc.)
12+
- **Flag Set**: A logical grouping of flags identified by a `flagSetId`
13+
- **Selector**: A query expression that filters flags by source, flag set, or other criteria
14+
- **Flag Set Metadata**: The selector information is "reflected" back in response metadata for transparency
15+
16+
## Source vs Flag Set Decoupling
17+
18+
### Before: Tight Coupling
19+
20+
Historically, each source provided exactly one flag set, and providers had to target specific sources:
21+
22+
```yaml
23+
# Old approach - targeting a specific source
24+
selector: "my-flag-source.json"
25+
```
26+
27+
### After: Flexible Flag Sets
28+
29+
Now, sources and flag sets are decoupled. A single source can contain multiple flag sets, and flag sets can span multiple sources:
30+
31+
```yaml
32+
# New approach - targeting a logical flag set
33+
selector: "flagSetId=project-42"
34+
```
35+
36+
## Flag Set Configuration
37+
38+
Flag sets are typically configured at the top level of a flag configuration, with all flags in that configuration inheriting the same `flagSetId`. This is the recommended approach for most use cases.
39+
40+
### Set-Level Configuration
41+
42+
The most common pattern is to set the `flagSetId` at the configuration level, where all flags inherit it:
43+
44+
```json
45+
{
46+
"metadata": {
47+
"flagSetId": "payment-service",
48+
"version": "v1.2.0"
49+
},
50+
"flags": {
51+
"new-checkout-flow": {
52+
"state": "ENABLED",
53+
"variants": {
54+
"on": true,
55+
"off": false
56+
},
57+
"defaultVariant": "on"
58+
},
59+
"stripe-integration": {
60+
"state": "DISABLED",
61+
"variants": { "on": true, "off": false },
62+
"defaultVariant": "off"
63+
}
64+
}
65+
}
66+
```
67+
68+
In this example, both `new-checkout-flow` and `stripe-integration` flags belong to the `payment-service` flag set.
69+
70+
### Metadata Inheritance and Override
71+
72+
Flagd uses a hierarchical metadata system:
73+
74+
1. **Set-Level Metadata**: Defined in the top-level `metadata` section, inherited by all flags
75+
2. **Flag-Level Metadata**: Defined in individual flag `metadata`, overrides set-level values for that flag
76+
3. **Merged Result**: Flag evaluations return merged metadata with flag-level taking precedence
77+
78+
### Flag-Level Overrides (Advanced)
79+
80+
For advanced use cases, individual flags can override the set-level `flagSetId`:
81+
82+
```json
83+
{
84+
"metadata": {
85+
"flagSetId": "payment-service",
86+
"team": "payments"
87+
},
88+
"flags": {
89+
"standard-feature": {
90+
"state": "ENABLED",
91+
"variants": { "on": true, "off": false },
92+
"defaultVariant": "on"
93+
// Inherits flagSetId: "payment-service"
94+
},
95+
"experimental-feature": {
96+
"metadata": {
97+
"flagSetId": "experiments", // Override: belongs to different set
98+
"owner": "research-team"
99+
},
100+
"state": "DISABLED",
101+
"variants": { "on": true, "off": false },
102+
"defaultVariant": "off"
103+
}
104+
}
105+
}
106+
```
107+
108+
In this example:
109+
- `standard-feature` inherits `flagSetId: "payment-service"` from set level
110+
- `experimental-feature` overrides to `flagSetId: "experiments"`
111+
- Both flags inherit `team: "payments"` (unless overridden at flag level)
112+
113+
## Flag Set Metadata "Reflection"
114+
115+
When you make a request with a selector, flagd "reflects" the selector information back in the response metadata. This provides transparency about what was actually queried and helps with debugging.
116+
117+
### Example
118+
119+
**Request with selector:**
120+
```
121+
Selector: "flagSetId=project-42"
122+
```
123+
124+
**Response includes reflected metadata:**
125+
```json
126+
{
127+
"flags": { /* ... */ },
128+
"metadata": {
129+
"flagSetId": "project-42"
130+
}
131+
}
132+
```
133+
134+
This helps you:
135+
- Verify that your selector was parsed correctly
136+
- Debug complex selector queries
137+
- Understand exactly what flags were returned
138+
- Audit flag access patterns
139+
140+
## Use Cases
141+
142+
### Multi-Tenant Applications
143+
144+
```yaml
145+
# Tenant A's flags
146+
selector: "flagSetId=tenant-a"
147+
148+
# Tenant B's flags
149+
selector: "flagSetId=tenant-b"
150+
```
151+
152+
### Environment Separation
153+
154+
```yaml
155+
# Development environment
156+
selector: "flagSetId=dev-features"
157+
158+
# Production environment
159+
selector: "flagSetId=prod-features"
160+
```
161+
162+
### Feature Team Isolation
163+
164+
```yaml
165+
# Payment team's flags
166+
selector: "flagSetId=payments"
167+
168+
# User interface team's flags
169+
selector: "flagSetId=ui-components"
170+
```
171+
172+
### Legacy Source-Based Selection
173+
174+
```yaml
175+
# Still supported for backward compatibility
176+
selector: "source=legacy-config.json"
177+
```
178+
179+
## Best Practices
180+
181+
1. **Use Flag Sets for Logical Grouping**: Prefer `flagSetId` over `source` for new deployments
182+
2. **Plan Your Flag Set Strategy**: Design flag sets around logical boundaries (teams, features, environments)
183+
3. **Leverage Metadata**: Use metadata for debugging and auditing
184+
5. **Document Your Schema**: Clearly document your flag set naming conventions for your team
185+
186+
## Migration Considerations
187+
188+
The selector enhancement maintains full backward compatibility. See the [migration guide](../guides/migrating-to-flag-sets.md) for detailed guidance on transitioning from source-based to flag-set-based selection patterns.
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
# Migrating to Flag Sets
2+
3+
This guide helps you transition from source-based selector patterns to flag-set-based patterns, taking advantage of flagd's enhanced selector capabilities while maintaining backward compatibility.
4+
5+
## Understanding the Change
6+
7+
### Before: Source-Based Selection
8+
9+
In the traditional approach, providers targeted specific sources:
10+
11+
```yaml
12+
# Provider configuration targeting a source file
13+
selector: "config/my-flags.json"
14+
```
15+
16+
This created tight coupling between providers and sources:
17+
- Providers had to know which source contained their flags
18+
- Moving flags between sources required provider reconfiguration
19+
- One source could only serve one logical set of flags
20+
21+
### After: Flag Set-Based Selection
22+
23+
With flag sets, providers target logical groupings of flags:
24+
25+
```yaml
26+
# Provider configuration targeting a flag set
27+
selector: "flagSetId=my-application"
28+
```
29+
30+
This provides flexibility:
31+
- Providers are decoupled from sources
32+
- Sources can contain multiple flag sets
33+
- Flag sets can span multiple sources
34+
- No breaking changes - old selectors still work
35+
36+
## Migration Process
37+
38+
### Step 1: Add Flag Set IDs to Configurations
39+
40+
Add `flagSetId` to your flag configurations at the set level:
41+
42+
**Before:**
43+
```json
44+
{
45+
"flags": {
46+
"feature-a": {
47+
"state": "ENABLED",
48+
"variants": {"on": true, "off": false},
49+
"defaultVariant": "on"
50+
}
51+
}
52+
}
53+
```
54+
55+
**After:**
56+
```json
57+
{
58+
"metadata": {
59+
"flagSetId": "my-application"
60+
},
61+
"flags": {
62+
"feature-a": {
63+
"state": "ENABLED",
64+
"variants": {"on": true, "off": false},
65+
"defaultVariant": "on"
66+
}
67+
}
68+
}
69+
```
70+
71+
### Step 2: Update Provider Configurations
72+
73+
Change provider selectors from source-based to flag set-based:
74+
75+
```java
76+
// Before
77+
new FlagdProvider(FlagdOptions.builder()
78+
.selector("config/app-flags.json").build());
79+
80+
// After
81+
new FlagdProvider(FlagdOptions.builder()
82+
.selector("flagSetId=my-application").build());
83+
```
84+
85+
### Step 3: Verify Migration
86+
87+
Test that selectors work correctly and check metadata reflection:
88+
89+
```bash
90+
curl -H "Flagd-Selector: flagSetId=my-application" \
91+
http://localhost:8014/ofrep/v1/evaluate/flags
92+
```
93+
94+
## Flag Set Organization Patterns
95+
96+
**By Application/Service:**
97+
```yaml
98+
flagSetId: "user-service" # All user-related flags
99+
flagSetId: "payment-service" # All payment-related flags
100+
```
101+
102+
**By Environment:**
103+
```yaml
104+
flagSetId: "development" # Dev-specific flags
105+
flagSetId: "production" # Production flags
106+
```
107+
108+
**By Team:**
109+
```yaml
110+
flagSetId: "frontend-team" # Frontend features
111+
flagSetId: "backend-team" # Backend features
112+
```
113+
114+
Choose the pattern that best matches your deployment and organizational structure.
115+
116+
## Testing and Rollback
117+
118+
**Test Migration:**
119+
```bash
120+
# Verify new selector works
121+
curl -H "Flagd-Selector: flagSetId=my-app" \
122+
http://localhost:8014/ofrep/v1/evaluate/flags
123+
124+
# Check backward compatibility
125+
curl -H "Flagd-Selector: config/legacy-flags.json" \
126+
http://localhost:8014/ofrep/v1/evaluate/flags
127+
```
128+
129+
**Rollback if Needed:**
130+
- Revert provider configurations to source-based selectors
131+
- Keep `flagSetId` metadata in flag configurations for future attempts
132+
- Use metadata reflection to debug issues
133+
134+
## Common Issues
135+
136+
**No flags returned**: Check that `flagSetId` in selector matches flag configuration exactly
137+
138+
**Wrong flags returned**: Look for flag-level `flagSetId` overrides or header/body selector conflicts
139+
140+
**Selector ignored**: Verify selector syntax is correct (`flagSetId=value`, not `flagSetId:value`)
141+
142+
## Best Practices
143+
144+
- **Group logically**: Organize flags by service, environment, or team
145+
- **Name consistently**: Use clear, descriptive flag set names
146+
- **Test first**: Validate migration in non-production environments
147+
- **Use metadata reflection**: Check reflected metadata for debugging
148+
149+
## FAQ
150+
151+
**Q: Do I have to migrate?**
152+
A: No, source-based selectors continue to work. Migration is optional but recommended.
153+
154+
**Q: Can flag sets span multiple sources?**
155+
A: Yes, multiple sources can contribute flags to the same flag set.
156+
157+
**Q: Can I mix selector types?**
158+
A: Yes, different providers can use different selector patterns.
159+
160+
## Additional Resources
161+
162+
- [Selector Concepts](../concepts/selectors.md) - Understanding selectors and flag sets
163+
- [Selector Syntax Reference](../reference/selector-syntax.md) - Complete syntax documentation
164+
- [ADR: Decouple Flag Source and Set](../architecture-decisions/decouple-flag-source-and-set.md) - Technical decision rationale

0 commit comments

Comments
 (0)