Skip to content
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 16 additions & 16 deletions docs/architecture-decisions/flag-type.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,45 +12,44 @@ updated: 2025-08-14

Currently, `flagd` has inconsistent behavior in type validation between its `Resolve<T>` and `ResolveAll` API methods. The `Resolve<T>` method validates the evaluated flag variant against the type `T` requested by the client, while `ResolveAll` validates it against the type of the `defaultVariant` specified in the flag's definition. This discrepancy can lead to situations where a flag evaluation succeeds with one method but fails with the other, depending on the evaluation context and the variant returned. This inconsistent behavior is further detailed in bug report #1481.

The root cause of this issue is the absence of a dedicated, authoritative type definition for the flag itself. Instead, the type is inferred from the `defaultVariant` or API itself (`T` from `Resolve<T>`) , which is not always a reliable source of truth for all possible variants. This can lead to unexpected errors and make it difficult for developers to debug their feature flags.
The root cause of this issue is the absence of a dedicated, authoritative type definition for the flag itself. Instead, the type is inferred from the `defaultVariant` or API itself (`T` from `Resolve<T>`) , which is not always a reliable source of truth for all possible variants. This issue is compounded by the planned support for code-defined defaults (as detailed in the [Support Code Default ADR](./support-code-default.md)), which allows the `defaultVariant` to be `null`. This makes it impossible to infer the flag's type from the `defaultVariant`, increasing the risk of runtime errors.


## Requirements

* The new `type` field in the flag definition must be optional to ensure backward compatibility.
* If the `type` field is present, `flagd` must validate that all variants of the flag conform to this type during initialization.
* The new `flagdType`field in the flag definition must be optional to ensure backward compatibility.
* If the `flagdType`field is present, `flagd` must validate that all variant values of the flag conform to this type during initialization.
* Type mismatches found during initialization must be reported as errors.
* The `Resolve<T>` and `ResolveAll` methods must use the `type` field for validation when it is available.
* The `Resolve<T>` and `ResolveAll` methods must use the `flagdType`field for validation when it is available.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the expected behavior? How does this behavior relate to flagd providers?

Providers currently try to convert the returned value to the requested evaluation type. They return an evaluation error when conversion between resolved and requested type is not supported by the underlying type system.

You can for example, call evaluateStringFlag(...) on a boolean flag and if the boolean value can be converted to a String, it will do so.
Do we expect with this change, that the before mentioned example should now return an evaluation error in flagd? If yes, this behavior should be consistent in flagd providers, so there is no behavioral difference between RPC and inProcess evaluation modes.

What do we expect when calling Resolve<Float> on a flag with flagType: "integer". Should this fail?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a section about float/integer types.

* The implementation must be consistent with the OpenFeature specification and the flag manifest schema.


## Considered Options

* **Consistent `defaultVariant` Validation:** Align the behavior of `Resolve<T>` with `ResolveAll` by making `Resolve<T>` validate the evaluated variant against the type of the `defaultVariant`.
* **API Extension with Explicit Flag Type:** Introduce an optional `type` property to the flag definition to serve as the authoritative source for type validation.
* **API Extension with Explicit Flag Type:** Introduce an optional `flagdType`property to the flag definition to serve as the authoritative source for type validation.


## Proposal

This proposal is to extend the flag definition with an optional `type` property. This approach is chosen over simply aligning the `Resolve<T>` and `ResolveAll` validation because it addresses the root cause of the type inconsistency and provides a more robust, long-term solution.
This proposal is to extend the flag definition with an optional `flagdType`property. This approach is chosen over simply aligning the `Resolve<T>` and `ResolveAll` validation because it addresses the root cause of the type inconsistency and provides a more robust, long-term solution.

By introducing an explicit `type` field, it establishes a single source of truth for the flag's type, independent of its variants. This allows for early and consistent type validation during flag definition parsing, preventing type-related errors at runtime.
By introducing an explicit `flagdType`field, it establishes a single source of truth for the flag's type, independent of its variants. This allows for early and consistent type validation during flag definition parsing, preventing type-related errors at runtime.

The new `type` field will be optional to maintain backward compatibility with existing flag configurations. If the field is omitted, `flagd` will treat the flag as having `object`, and no type validation will be performed against the `defaultVariant`. When the `type` field is present, `flagd` will enforce that all variants of the flag conform to the specified type.
The new `flagdType`field will be optional to maintain backward compatibility with existing flag configurations. If the field is omitted, `flagd` will treat the flag as having `object`, and no type validation will be performed against the `defaultVariant`. When the `flagdType`field is present, `flagd` will enforce that all variants of the flag conform to the specified type.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add more details about how the validation would work?

I'm particularly interested in the cases that the literal values set to the variants do not fit with the flag type. Currently flagd seems to just cast the value to the type, with precision lost (this is perhaps more related to the JSONLogic engine impl).

CC @toddbaert


This change will make the behavior of `flagd` more predictable and reliable.


### API changes

The `flagd` flag definition will be updated to include an optional `type` property. This property will be a string enum with the following possible values: `"boolean"`, `"string"`, `"number"`, and `"object"`.

The `flagd` flag definition will be updated to include an optional `flagdType`property. This property will be a string enum with the following possible values: `"boolean"`, `"string"`, `"integer"`, `"float"`, and `"object"`. This aligns with the OpenFeature CLI and the flag manifest schema.

#### JSON Schema

The following changes will be made to the `schemas/json/flags.json` file:

1. A new `type` property will be added to the `flag` definition:
1. A new `flagdType`property will be added to the `flag` definition:

```json
"flag": {
Expand All @@ -63,7 +62,8 @@ The following changes will be made to the `schemas/json/flags.json` file:
"enum": [
"boolean",
"string",
"number",
"integer",
"float",
"object"
]
},
Expand All @@ -75,7 +75,7 @@ The following changes will be made to the `schemas/json/flags.json` file:
}
```

2. The `booleanFlag`, `stringFlag`, `numberFlag`, and `objectFlag` definitions will be updated to enforce the `type` property:
2. The `booleanFlag`, `stringFlag`, `integerFlag`, `floatFlag`, and `objectFlag` definitions will be updated to enforce the `flagdType`property:

```json
"booleanFlag": {
Expand All @@ -97,7 +97,7 @@ The following changes will be made to the `schemas/json/flags.json` file:
}
```

Similar changes will be made to `stringFlag`, `numberFlag`, and `objectFlag` to enforce their respective types.
Similar changes will be made to `stringFlag`, `integerFlag`, `floatFlag`, and `objectFlag` to enforce their respective types.

### Consequences

Expand All @@ -114,12 +114,12 @@ Similar changes will be made to `stringFlag`, `numberFlag`, and `objectFlag` to
### Timeline

* **Phase 1: Core Implementation**
* Update the `flagd` core to support the new `type` field.
* Update the `flagd` core to support the new `flagdType`field.
* Implement the type validation logic.
* Update the JSON schema.
* Add unit and integration tests.
* **Phase 2: SDK Updates**
* Update all `flagd` SDKs to support the new `type` field.
* Update all `flagd` SDKs to support the new `flagdType`field.
* Update flag manifest
* **Phase 3: Documentation**
* Update the `flagd` documentation to reflect the changes.
Expand Down