Skip to content

Commit 6072909

Browse files
authored
docs: add flag config adr (#1637)
Another "retrospective" ADR, like the cucumber one. This explains our JSONLogic choice, shared `$evaluators`, as well as the the semantics of flagd's use of OpenFeature's [resolution reasons](https://openfeature.dev/specification/types#resolution-reason). --------- Signed-off-by: Todd Baert <todd.baert@dynatrace.com>
1 parent c90c91f commit 6072909

File tree

1 file changed

+155
-0
lines changed

1 file changed

+155
-0
lines changed
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
---
2+
status: accepted
3+
author: Todd Baert
4+
created: 2025-06-05
5+
updated: 2025-06-05
6+
---
7+
8+
# Flag and Targeting Configuration
9+
10+
## Background
11+
12+
Feature flag systems require a flexible, safe, and portable way to express targeting rules that can evaluate contextual data to determine which variant of a feature to serve.
13+
14+
flagd's targeting system was designed with several key requirements:
15+
16+
## Requirements
17+
18+
- **Language agnostic**: Rules must be portable across different programming languages, ideally relying on existing expression language(s)
19+
- **Safe evaluation**: No arbitrary code execution or system access
20+
- **Deterministic**: Same inputs must always produce same outputs
21+
- **Extensible**: Support for the addition of domain-specific operations relevant to feature flags
22+
- **Developer and machine friendly**: Human-readable, easily validated, and easily serialized
23+
24+
## Proposal
25+
26+
### JSON Logic as the Foundation
27+
28+
flagd chose **JSON Logic** as its core evaluation engine, implementing a modified version with custom extensions.
29+
This provides a secure, portable foundation where rules are expressed as JSON objects with operators as keys and parameters as values.
30+
31+
#### Benefits realized
32+
33+
- Rules can be stored in databases, transmitted over networks, shared between frontend/backend, and embedded in Kubernetes custom resources
34+
- No eval() or code injection risks - computations are deterministic and sand-boxed
35+
- Implementations exist in most languages
36+
37+
#### Overview
38+
39+
The system provides two tiers of operators:
40+
41+
##### Primitive JSON Logic Operators (inherited from the JSONLogic)
42+
43+
- Logical: `and`, `or`, `!`, `!!`
44+
- Comparison: `==`, `!=`, `>`, `<`, etc
45+
- Arithmetic: `+`, `-`, `*`, `/`, `%`
46+
- Array operations: `in`, `map`, `filter`, etc
47+
- String operations: `cat`, `substr`, etc
48+
- Control flow: `if`
49+
- Assignment and extraction: `var`
50+
51+
##### Custom flagd Extensions
52+
53+
- `fractional`: Deterministic percentage-based distribution using murmur3 hashing
54+
- `starts_with`/`ends_with`: String prefix/suffix matching for common patterns
55+
- `sem_ver`: Semantic version comparisons with standard (npm-style) operators
56+
- `$ref`: Reference to shared evaluators for DRY principle
57+
58+
##### Evaluation Context and Automatic Enrichment
59+
60+
flagd automatically injects critical context values:
61+
62+
##### System-provided context
63+
64+
- `$flagd.flagKey`: The flag being evaluated (available v0.6.4+)
65+
- `$flagd.timestamp`: Unix timestamp of evaluation (available v0.6.7+)
66+
67+
This enables sophisticated targeting rules that can reference the flag itself or time-based conditions without requiring client-side context.
68+
69+
##### Reason Code System for Transparency
70+
71+
flagd returns specific reason codes with every evaluation to indicate how the decision was made:
72+
73+
1. **STATIC**: Flag has no targeting rules, and can be safely cached
74+
2. **TARGETING_MATCH**: Targeting rules matched and returned a variant
75+
3. **DEFAULT**: Targeting rules evaluated to null, fell back to default
76+
4. **CACHED**: Value retrieved from provider cache (RPC mode only)
77+
5. **ERROR**: Evaluation failed due to invalid configuration
78+
79+
This transparency enables:
80+
81+
- Appropriate caching strategies (only STATIC flags are cached)
82+
- Improved debugging, telemetry, and monitoring of flag behavior
83+
84+
##### Shared Evaluators for Reusability
85+
86+
The `$evaluators` top-level property enables shared targeting logic:
87+
88+
```json
89+
{
90+
"$evaluators": {
91+
"isEmployee": {
92+
"ends_with": [{"var": "email"}, "@company.com"]
93+
}
94+
},
95+
"flags": {
96+
"feature-x": {
97+
"state": "ENABLED",
98+
"defaultVariant": "enabled",
99+
"variants": {
100+
"enabled": true,
101+
"disabled": false
102+
},
103+
"targeting": {
104+
"if": [{"$ref": "isEmployee"}, "enabled", "disabled"]
105+
}
106+
}
107+
}
108+
}
109+
```
110+
111+
##### Intelligent Caching Strategy
112+
113+
Only flags with reason **STATIC** are cached, as they have deterministic outputs. This ensures:
114+
115+
- Maximum cache efficiency for simple toggles
116+
- Fresh evaluation for complex targeting rules
117+
- Cache invalidation on configuration changes
118+
119+
##### Schema-Driven Configuration
120+
121+
Two schemas validate flag configurations:
122+
123+
- `https://flagd.dev/schema/v0/flags.json`: Overall flag structure
124+
- `https://flagd.dev/schema/v0/targeting.json`: Targeting rule validation
125+
126+
These enable:
127+
128+
- IDE support with autocomplete
129+
- Run-time and build-time validation
130+
- Separate validation of rules and overall configuration if desired
131+
132+
## Considered Options
133+
134+
- **Custom DSL**: Would require parsers in every language
135+
- **JavaScript/Lua evaluation**: Security risks and language lock-in
136+
- **CEL**: limited number of implementations at time of decision, can't be directly parsed/validated when embedded in Kubernetes resources
137+
138+
## Consequences
139+
140+
### Positive
141+
142+
- Good, because implementations exist across languages
143+
- Good, because, no code injection or system access possible
144+
- Good, because combined with JSON schemas, we have rich IDE support
145+
- Good, because JSON is easily serialized and also can be represented/embedded in YAML
146+
147+
### Negative
148+
149+
- Bad, JSONLogic syntax can be cumbersome when rules are complex
150+
- Bad, hard to debug
151+
152+
## Conclusion
153+
154+
flagd's targeting configuration system represents a thoughtful balance between safety, portability, and capability.
155+
By building on JSON Logic and extending it with feature-flag-specific operators, flagd achieves remarkable flexibility while maintaining security and performance.

0 commit comments

Comments
 (0)