Skip to content
Open
Changes from 13 commits
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
130 changes: 130 additions & 0 deletions docs/architecture-decisions/flag-type.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
---
status: proposed
author: @andreyturkov
created: 2025-08-14
updated: 2025-08-14
---

# Extending Flag Definition with a Type Property

## Background

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 problem is getting worse by the planned support for code-defined defaults (as detailed in the [Support Code Default ADR](https://github.com/open-feature/flagd/blob/main/docs/architecture-decisions/support-code-default.md)), which allows the `defaultVariant` to be `null`.
This makes it impossible to resolve the flag's type from the `defaultVariant`, increasing the risk of runtime errors.

## Requirements

* The new `flagType`field in the flag definition must be optional to ensure backward compatibility.
* If the `flagType`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 `flagType`field for validation when it is available.
* 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 `flagType`property to the flag definition to serve as the single source for type validation.

## Proposal

This proposal is to extend the flag definition with an optional `flagType`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 `flagType`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 `flagType`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 `flagType`field is present, `flagd` will enforce that all variants of the flag conform to the specified type.

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 `flagType`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.
Copy link
Member

@toddbaert toddbaert Aug 21, 2025

Choose a reason for hiding this comment

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

I'm not 100% sure it's a good idea to differentiate between float/int for this purpose. A couple reasons:

  • I think we could easily write a JSON schema to enforce that flags with "flagType":boolean only have variants of type boolean, and "flagType":string flags only have variants of type string, etc, etc... but I don't think we can enforce that ints are ints and not floats (we might be able to do this with multipleOf but I'd like to see a PoC first).
  • Our providers don't care about float/int as it is; they convert instead of indicating a type error.
  • JSON itself doesn't distinguish between numeric types, so attempting to do so might be fraught.

Very interested in other thoughts on this.

Copy link
Member

Choose a reason for hiding this comment

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

The main reason to have both float and int is to match the flag manifest used in the OpenFeature cli. I think it's important to keep them in sync. However, we may need to support casting from at least int to float.

Copy link
Contributor

Choose a reason for hiding this comment

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

Our providers don't care about float/int as it is; they convert instead of indicating a type error.

I see the type system of JSONLogic as a limitation, as Number is essentially an IEEE 754 Double.

Conversions between Double and Integer64 may lose precision in both directions. I'd like to see a stronger typing is enforced if possible (depending on the language), to prevent the case that the client gets a different value than what's put in the evaluation rules, which can be quite dangerous for some situations of using Number flags, for example, when the flag represents a GCP project number

Copy link
Member

Choose a reason for hiding this comment

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

Do you think this is something we need to enhance the OpenFeature specification to support?

IMO, I think numeric precision is a fuzzy part of the spec, mostly because it's non-trivial to write language-neutral specification about that sort of thing.

Copy link
Contributor

@tangenti tangenti Aug 26, 2025

Choose a reason for hiding this comment

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

I don't have enough contexts and knowledge to form a mature opinion for OpenFeature spec.... I guess we could explore the extension in flagd and OFREP first and we'll learn more about the feasibility. For now, I think drawing the boundary for each type in the flagd can be helpful.

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.


#### JSON Schema

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

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

```json
"flag": {
"type": "object",
"properties": {
"flagType": {
"title": "Flag Type",
"description": "The type of the flag. If specified, all variants must conform to this type.",
"type": "string",
"enum": [
"boolean",
"string",
"integer",
"float",
"object"
]
},
"state": {
...
},
...
}
}
```

1. The `booleanFlag`, `stringFlag`, `integerFlag`, `floatFlag`, and `objectFlag` definitions will be updated to enforce the `flagType`property:

```json
"booleanFlag": {
"allOf": [
{
"$ref": "#/definitions/flag"
},
{
"$ref": "#/definitions/booleanVariants"
},
{
"properties": {
"flagType": {
"const": "boolean"
}
}
}
]
}
```

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

### Consequences

#### The good

* It improves the reliability and predictability of flag evaluations.
* It allows for early error detection of type mismatches.
* It improves the developer experience by making the API more explicit.

#### The bad

* It adds a new field to the flag definition, which developers need to be aware of.

### Timeline

* **Phase 1: Core Implementation**
* Update the JSON schema.
* Update the `flagd` core to support the new `flagType`field.
* Implement the type validation logic.
* Add unit and integration tests.
* **Phase 2: Documentation**
* Update the `flagd` documentation to reflect the changes.

## More Information

* **Bug Report:** [https://github.com/open-feature/flagd/issues/1481](https://github.com/open-feature/flagd/issues/1481)
* **Flag schema** [https://flagd.dev/schema/v0/flags.json](https://flagd.dev/schema/v0/flags.json)
* **Flag Manifest Schema:** [https://raw.githubusercontent.com/open-feature/cli/refs/heads/main/schema/v0/flag-manifest.json](https://raw.githubusercontent.com/open-feature/cli/refs/heads/main/schema/v0/flag-manifest.json)