Skip to content

Commit 007502b

Browse files
toddbaertskyerusJustin Abrahmsmoredipjonathannorris
authored
feat!: add static/dynamic context (#171)
Signed-off-by: Todd Baert <[email protected]> Co-authored-by: Skye Gill <[email protected]> Co-authored-by: Justin Abrahms <[email protected]> Co-authored-by: Pete Hodgson <[email protected]> Co-authored-by: Jonathan Norris <[email protected]> Co-authored-by: Kavindu Dodanduwa <[email protected]>
1 parent 2c9344c commit 007502b

File tree

7 files changed

+415
-106
lines changed

7 files changed

+415
-106
lines changed

specification.json

Lines changed: 186 additions & 65 deletions
Large diffs are not rendered by default.

specification/glossary.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ This document defines some terms that are used across this specification.
2222
- [Library Author](#library-author)
2323
- [Common](#common)
2424
- [Feature Flag SDK](#feature-flag-sdk)
25+
- [Client-Side SDK](#client-side-sdk)
26+
- [Server-Side SDK](#server-side-sdk)
2527
- [Feature Flag API](#feature-flag-api)
2628
- [Evaluation API](#evaluation-api)
2729
- [Flag Management System](#flag-management-system)
@@ -39,6 +41,9 @@ This document defines some terms that are used across this specification.
3941
- [Targeting Key](#targeting-key)
4042
- [Fractional Evaluation](#fractional-evaluation)
4143
- [Rule](#rule)
44+
- [SDK Paradigms](#sdk-paradigms)
45+
- [Dynamic-Context Paradigm](#dynamic-context-paradigm)
46+
- [Static-Context Paradigm](#static-context-paradigm)
4247

4348
<!-- tocstop -->
4449

@@ -76,6 +81,14 @@ The maintainer of a shared library which is a dependency of many applications or
7681

7782
The libraries used by the Application Author to implement feature flags in their application or service. The interfaces defined in these libraries adhere to the Feature Flag API.
7883

84+
### Client-Side SDK
85+
86+
An SDK which is built for usage in client applications (e.g. single-page web applications), and typically uses the [static-context paradigm](#static-context-paradigm).
87+
88+
### Server-Side SDK
89+
90+
An SDK which is built for usage in server applications (e.g. REST services), and typically uses the [dynamic-context paradigm](#dynamic-context-paradigm).
91+
7992
### Feature Flag API
8093

8194
The interfaces and abstractions used by authors (Application, Integration, Provider).
@@ -163,3 +176,25 @@ Pseudorandomly resolve flag values using a context property, such as a targeting
163176
### Rule
164177

165178
A rule is some criteria that's used to determine which variant a particular context should be mapped to.
179+
180+
## SDK Paradigms
181+
182+
Feature flag frameworks have SDKs which operate in two distinct paradigms: those designed for use with a single user client application (e.g. mobile phones, single-page web apps), and those designed for multi-user applications, such as web server applications. Some parts of the OpenFeature specification diverge depending on the paradigm.
183+
184+
### Dynamic-Context Paradigm
185+
186+
Server-side applications typically perform flag evaluations on behalf of many users, with each request or event being associated with a particular user or client. For this reason, server frameworks typically operate similarly to this:
187+
188+
- the application is initialized with some static context (geography, service name, hostname, etc)
189+
- with each request or event, relevant dynamic context (for example, user session data, unique user identifiers) is provided to flag evaluations
190+
191+
### Static-Context Paradigm
192+
193+
In contrast to server-side or other service-type applications, client side applications typically operate in the context of a single user. Most feature flagging libraries for these applications have been designed with this in mind. Frequently, client/web libraries operate similarly to this:
194+
195+
- an initialization occurs, which fetches evaluated flags in bulk for a given context (user)
196+
- the evaluated flags are cached in the library
197+
- flag evaluations take place against this cache, without a need to provide context (context was already used to evaluate flags in bulk)
198+
- libraries provide a mechanism to update context (e.g. if a user logs in), meaning cached evaluations are no longer valid and must be re-evaluated, frequently involving a network request or I/O operation
199+
200+
Not all client libraries work this way, but generally, libraries that accept dynamic context per evaluation can build providers which conform to this model with relative ease, while the reverse is not true.

specification/sections/01-flag-evaluation.md

Lines changed: 84 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,13 @@ client.getMetadata().getName(); // "my-client"
130130

131131
[![hardening](https://img.shields.io/static/v1?label=Status&message=hardening&color=yellow)](https://github.com/open-feature/spec/tree/main/specification#hardening)
132132

133-
#### Requirement 1.3.1
133+
#### Condition 1.3.1
134+
135+
> The implementation uses the dynamic-context paradigm.
136+
137+
see: [dynamic-context paradigm](../glossary.md#dynamic-context-paradigm)
138+
139+
##### Conditional Requirement 1.3.1.1
134140

135141
> The `client` **MUST** provide methods for typed flag evaluation, including boolean, numeric, string, and structure, with parameters `flag key` (string, required), `default value` (boolean | number | string | structure, required), `evaluation context` (optional), and `evaluation options` (optional), which returns the flag value.
136142
@@ -152,10 +158,37 @@ See [evaluation context](./03-evaluation-context.md) for details.
152158

153159
#### Condition 1.3.2
154160

155-
> The implementation language differentiates between floating-point numbers and integers.
161+
[![experimental](https://img.shields.io/static/v1?label=Status&message=experimental&color=orange)](https://github.com/open-feature/spec/tree/main/specification#experimental)
162+
163+
> The implementation uses the static-context paradigm.
164+
165+
see: [static-context paradigm](../glossary.md#static-context-paradigm)
156166

157167
##### Conditional Requirement 1.3.2.1
158168

169+
> The `client` **MUST** provide methods for typed flag evaluation, including boolean, numeric, string, and structure, with parameters `flag key` (string, required), `default value` (boolean | number | string | structure, required), and `evaluation options` (optional), which returns the flag value.
170+
171+
```typescript
172+
// example boolean flag evaluation
173+
boolean myBool = client.getBooleanValue('bool-flag', false);
174+
175+
// example overloaded string flag evaluation with optional params
176+
string myString = client.getStringValue('string-flag', 'N/A', options);
177+
178+
// example number flag evaluation
179+
number myNumber = client.getNumberValue('number-flag', 75);
180+
181+
// example overloaded structure flag evaluation with optional params
182+
MyStruct myStruct = client.getObjectValue<MyStruct>('structured-flag', { text: 'N/A', percentage: 75 }, options);
183+
```
184+
185+
186+
#### Condition 1.3.3
187+
188+
> The implementation language differentiates between floating-point numbers and integers.
189+
190+
##### Conditional Requirement 1.3.3.1
191+
159192
> The client **SHOULD** provide functions for floating-point numbers and integers, consistent with language idioms.
160193
161194
```java
@@ -166,15 +199,21 @@ long getFloatValue(String flag, long defaultValue);
166199

167200
See [types](../types.md) for details.
168201

169-
#### Requirement 1.3.3
202+
#### Requirement 1.3.4
170203

171204
> The `client` **SHOULD** guarantee the returned value of any typed flag evaluation method is of the expected type. If the value returned by the underlying provider implementation does not match the expected type, it's to be considered abnormal execution, and the supplied `default value` should be returned.
172205
173206
### 1.4. Detailed Flag Evaluation
174207

175208
[![hardening](https://img.shields.io/static/v1?label=Status&message=hardening&color=yellow)](https://github.com/open-feature/spec/tree/main/specification#hardening)
176209

177-
#### Requirement 1.4.1
210+
#### Condition 1.4.1
211+
212+
> The implementation uses the dynamic-context paradigm.
213+
214+
see: [dynamic-context paradigm](../glossary.md#dynamic-context-paradigm)
215+
216+
##### Conditional Requirement 1.4.1.1
178217

179218
> The `client` **MUST** provide methods for detailed flag value evaluation with parameters `flag key` (string, required), `default value` (boolean | number | string | structure, required), `evaluation context` (optional), and `evaluation options` (optional), which returns an `evaluation details` structure.
180219
@@ -193,69 +232,96 @@ FlagEvaluationDetails<MyStruct> myStructDetails = client.getObjectDetails<MyStru
193232

194233
```
195234

196-
#### Requirement 1.4.2
235+
#### Condition 1.4.2
236+
237+
[![experimental](https://img.shields.io/static/v1?label=Status&message=experimental&color=orange)](https://github.com/open-feature/spec/tree/main/specification#experimental)
238+
239+
> The implementation uses the static-context paradigm.
240+
241+
see: [static-context paradigm](../glossary.md#static-context-paradigm)
242+
243+
##### Conditional Requirement 1.4.2.1
244+
245+
> The `client` **MUST** provide methods for detailed flag value evaluation with parameters `flag key` (string, required), `default value` (boolean | number | string | structure, required), and `evaluation options` (optional), which returns an `evaluation details` structure.
246+
247+
```typescript
248+
// example detailed boolean flag evaluation
249+
FlagEvaluationDetails<boolean> myBoolDetails = client.getBooleanDetails('bool-flag', false);
250+
251+
// example detailed string flag evaluation
252+
FlagEvaluationDetails<string> myStringDetails = client.getStringDetails('string-flag', 'N/A', options);
253+
254+
// example detailed number flag evaluation
255+
FlagEvaluationDetails<number> myNumberDetails = client.getNumberDetails('number-flag', 75);
256+
257+
// example detailed structure flag evaluation
258+
FlagEvaluationDetails<MyStruct> myStructDetails = client.getObjectDetails<MyStruct>('structured-flag', { text: 'N/A', percentage: 75 }, options);
259+
260+
```
261+
262+
#### Requirement 1.4.3
197263

198264
> The `evaluation details` structure's `value` field **MUST** contain the evaluated flag value.
199265
200-
#### Condition 1.4.3
266+
#### Condition 1.4.4
201267

202268
> The language supports generics (or an equivalent feature).
203269
204-
##### Conditional Requirement 1.4.3.1
270+
##### Conditional Requirement 1.4.4.1
205271

206272
> The `evaluation details` structure **SHOULD** accept a generic argument (or use an equivalent language feature) which indicates the type of the wrapped `value` field.
207273
208-
#### Requirement 1.4.4
274+
#### Requirement 1.4.5
209275

210276
> The `evaluation details` structure's `flag key` field **MUST** contain the `flag key` argument passed to the detailed flag evaluation method.
211277
212-
#### Requirement 1.4.5
278+
#### Requirement 1.4.6
213279

214280
> In cases of normal execution, the `evaluation details` structure's `variant` field **MUST** contain the value of the `variant` field in the `flag resolution` structure returned by the configured `provider`, if the field is set.
215281
216-
#### Requirement 1.4.6
282+
#### Requirement 1.4.7
217283

218284
> In cases of normal execution, the `evaluation details` structure's `reason` field **MUST** contain the value of the `reason` field in the `flag resolution` structure returned by the configured `provider`, if the field is set.
219285
220-
#### Requirement 1.4.7
286+
#### Requirement 1.4.8
221287

222288
> In cases of abnormal execution, the `evaluation details` structure's `error code` field **MUST** contain an `error code`.
223289
224290
See [error code](../types.md#error-code) for details.
225291

226-
#### Requirement 1.4.8
292+
#### Requirement 1.4.9
227293

228294
> In cases of abnormal execution (network failure, unhandled error, etc) the `reason` field in the `evaluation details` **SHOULD** indicate an error.
229295
230-
#### Requirement 1.4.9
296+
#### Requirement 1.4.10
231297

232298
> Methods, functions, or operations on the client **MUST NOT** throw exceptions, or otherwise abnormally terminate. Flag evaluation calls must always return the `default value` in the event of abnormal execution. Exceptions include functions or methods for the purposes for configuration or setup.
233299
234300
Configuration code includes code to set the provider, instantiate providers, and configure the global API object.
235301

236-
#### Requirement 1.4.10
302+
#### Requirement 1.4.11
237303

238304
> In the case of abnormal execution, the client **SHOULD** log an informative error message.
239305
240306
Implementations may define a standard logging interface that can be supplied as an optional argument to the client creation function, which may wrap standard logging functionality of the implementation language.
241307

242-
#### Requirement 1.4.11
308+
#### Requirement 1.4.12
243309

244310
> The `client` **SHOULD** provide asynchronous or non-blocking mechanisms for flag evaluation.
245311
246312
It's recommended to provide non-blocking mechanisms for flag evaluation, particularly in languages or environments wherein there's a single thread of execution.
247313

248-
#### Requirement 1.4.12
314+
#### Requirement 1.4.13
249315

250316
> In cases of abnormal execution, the `evaluation details` structure's `error message` field **MAY** contain a string containing additional details about the nature of the error.
251317
252-
#### Requirement 1.4.13
318+
#### Requirement 1.4.14
253319

254320
> If the `flag metadata` field in the `flag resolution` structure returned by the configured `provider` is set, the `evaluation details` structure's `flag metadata` field **MUST** contain that value. Otherwise, it **MUST** contain an empty record.
255321
256322
This `flag metadata` field is intended as a mechanism for providers to surface additional information about a feature flag (or its evaluation) beyond what is defined within the OpenFeature spec itself. The primary consumer of this information is a provider-specific hook.
257323

258-
#### Condition 1.4.14
324+
#### Condition 1.4.15
259325

260326
> The implementation language supports a mechanism for marking data as immutable.
261327

specification/sections/02-providers.md

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ The value of the variant field might only be meaningful in the context of the fl
7777

7878
#### Requirement 2.2.5
7979

80-
> The `provider` **SHOULD** populate the `resolution details` structure's `reason` field with `"STATIC"`, `"DEFAULT",` `"TARGETING_MATCH"`, `"SPLIT"`, `"CACHED"`, `"DISABLED"`, `"UNKNOWN"`, `"ERROR"` or some other string indicating the semantic reason for the returned flag value.
80+
> The `provider` **SHOULD** populate the `resolution details` structure's `reason` field with `"STATIC"`, `"DEFAULT",` `"TARGETING_MATCH"`, `"SPLIT"`, `"CACHED"`, `"DISABLED"`, `"UNKNOWN"`, `"STALE"`, `"ERROR"` or some other string indicating the semantic reason for the returned flag value.
8181
8282
As indicated in the definition of the [`resolution details`](../types.md#resolution-details) structure, the `reason` should be a string. This allows providers to reflect accurately why a flag was resolved to a particular value.
8383

@@ -245,6 +245,30 @@ class MyProvider implements Provider, AutoDisposable {
245245
void dispose() {
246246
// close connections, terminate threads or timers, etc...
247247
}
248+
```
249+
250+
### 2.6. Provider context reconciliation
251+
252+
[![experimental](https://img.shields.io/static/v1?label=Status&message=experimental&color=orange)](https://github.com/open-feature/spec/tree/main/specification#experimental)
253+
254+
Static-context focused providers may need a mechanism to understand when their cache of evaluated flags must be invalidated or updated. An `on context changed` handler can be defined which performs whatever operations are needed to reconcile the evaluated flags with the new context.
255+
256+
#### Requirement 2.6.1
257+
258+
> The provider **MAY** define an `on context changed` handler, which takes an argument for the previous context and the newly set context, in order to respond to an evaluation context change.
259+
260+
Especially in static-context implementations, providers and underlying SDKs may maintain state for a particular context.
261+
The `on context changed` handler provides a mechanism to update this state, often by re-evaluating flags in bulk with respect to the new context.
262+
263+
```java
264+
// MyProvider implementation of the onContextChanged function defined in Provider
265+
class MyProvider implements Provider {
266+
//...
267+
268+
onContextChanged(EvaluationContext oldContext, EvaluationContext newContext): void {
269+
// update context-sensitive cached flags, or otherwise react to the change in the global context
270+
}
271+
248272
//...
249273
}
250274
```

specification/sections/03-evaluation-context.md

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,39 @@ The key uniquely identifies a field in the `evaluation context` and it should be
4444

4545
### 3.2 Context levels and merging
4646

47-
#### Requirement 3.2.1
47+
#### Condition 3.2.1
48+
49+
> The implementation uses the dynamic-context paradigm.
50+
51+
see: [dynamic-context paradigm](../glossary.md#dynamic-context-paradigm)
52+
53+
##### Conditional Requirement 3.2.1.1
4854

4955
> The API, Client and invocation **MUST** have a method for supplying `evaluation context`.
5056
5157
API (global) `evaluation context` can be used to supply static data to flag evaluation, such as an application identifier, compute region, or hostname. Client and invocation `evaluation context` are ideal for dynamic data, such as end-user attributes.
5258

53-
#### Requirement 3.2.2
59+
#### Condition 3.2.2
60+
61+
[![experimental](https://img.shields.io/static/v1?label=Status&message=experimental&color=orange)](https://github.com/open-feature/spec/tree/main/specification#experimental)
62+
63+
> The implementation uses the static-context paradigm.
64+
65+
see: [static-context paradigm](../glossary.md#static-context-paradigm)
66+
67+
##### Conditional Requirement 3.2.2.1
68+
69+
> The API **MUST** have a method for setting the global `evaluation context`.
70+
71+
API (global) `evaluation context` can be used to supply data to flag evaluation, such as (but not limited to) user name, email, or user organization membership changes.
72+
73+
##### Conditional Requirement 3.2.2.2
74+
75+
> The Client and invocation **MUST NOT** have a method for supplying `evaluation context`.
76+
77+
In the static-context paradigm, context is global. The client and invocation cannot supply evaluation context.
78+
79+
#### Requirement 3.2.3
5480

5581
> Evaluation context **MUST** be merged in the order: API (global; lowest precedence) -> client -> invocation -> before hooks (highest precedence), with duplicate values being overwritten.
5682
@@ -66,3 +92,17 @@ flowchart LR
6692
client --> invocation
6793
invocation --> hook
6894
```
95+
96+
#### Condition 3.2.4
97+
98+
[![experimental](https://img.shields.io/static/v1?label=Status&message=experimental&color=orange)](https://github.com/open-feature/spec/tree/main/specification#experimental)
99+
100+
> The implementation uses the static-context paradigm.
101+
102+
see: [static-context paradigm](../glossary.md#static-context-paradigm)
103+
104+
##### Requirement 3.2.4.1
105+
106+
> When the global `evaluation context` is set, the `on context changed` handler **MUST** run.
107+
108+
The SDK implementation must run the `on context changed` handler on the registered provider whenever the global `evaluation context` is mutated.

0 commit comments

Comments
 (0)