Skip to content

Epic: Project-Scoped Export/Import #7125

@khvn26

Description

@khvn26

This epic defines implementation for project-level export and import.

Docs PR: #7114

Feature flags

  • project_import_export_identity_overrides — gates the "Include identity overrides" toggle in
    the export UI. Disabled by default. Enabled when the identity override import sub-issue lands.

Export format schema

The project export is a JSON object, vs. the env-level export which is a JSON array.

Entity field names match the OpenAPI spec exactly. The structural nesting mirrors the API. Features and segments are project-level definitions; environments hold the per-environment state. This avoids inventing export-only structural concepts.

Example

{
  "version": 1,
  "exported_at": "2026-04-02T18:00:00Z",

  "tags": [
    {
      "label": "core",
      "color": "#FF0000",
      "description": "Core features",
      "is_permanent": false
    }
  ],

  "segments": [
    {
      "name": "beta_users",
      "description": "Beta programme users",
      "rules": [
        {
          "type": "ALL",
          "rules": [],
          "conditions": [
            {
              "property_": "is_beta",
              "operator": "EQUAL",
              "value": "true"
            }
          ]
        }
      ]
    }
  ],

  "features": [
    {
      "name": "my_feature",
      "default_enabled": true,
      "is_server_key_only": false,
      "initial_value": "default_value",
      "description": "",
      "tags": ["core"],
      "multivariate_options": [
        {
          "type": "unicode",
          "string_value": "variant_a",
          "default_percentage_allocation": 33.33
        }
      ]
    }
  ],

  "environments": [
    {
      "name": "Production",
      "description": "Production environment",
      "minimum_change_request_approvals": 2,
      "allow_client_traits": true,
      "banner_text": null,
      "banner_colour": null,
      "hide_disabled_flags": null,
      "use_identity_composite_key_for_hashing": true,
      "hide_sensitive_data": false,
      "use_v2_feature_versioning": true,
      "use_identity_overrides_in_local_eval": true,

      "feature_states": [
        {
          "feature": "my_feature",
          "enabled": true,
          "feature_state_value": {
            "type": "unicode",
            "string_value": "prod_value"
          },
          "multivariate_feature_state_values": [
            {
              "multivariate_feature_option": 0,
              "percentage_allocation": 50
            }
          ]
        }
      ],

      "feature_segments": [
        {
          "feature": "my_feature",
          "segment": "beta_users",
          "priority": 0,
          "feature_state": {
            "enabled": true,
            "feature_state_value": {
              "type": "unicode",
              "string_value": "beta_value"
            },
            "multivariate_feature_state_values": []
          }
        }
      ],

      "identity_overrides": [
        {
          "identifier": "user123",
          "feature": "my_feature",
          "enabled": true,
          "feature_state_value": {
            "type": "unicode",
            "string_value": "custom_value"
          }
        }
      ]
    },

    {
      "name": "Staging",
      "feature_states": [],
      "feature_segments": []
    }
  ]
}

Key design decisions

  • All cross-references use names (environment name, segment name, tag label, feature name) — not IDs, so exports are portable across instances.
  • multivariate_feature_option in multivariate_feature_state_values is an index into the feature's multivariate_options array. In the API this is an integer FK; we reuse the field name but the semantics differ because IDs aren't portable. This is the only unavoidable divergence from the API.
  • version: 1 allows future schema evolution.
  • identity_overrides per environment is optional (only present when the user opts in).
  • tags is top-level and optional.

Matching on import (by name)

  • Features: matched by name (already unique per project via DB constraint).
  • Segments: matched by name. If duplicates exist in the target project, the first match is used. If duplicates exist in the export, all are imported.
  • Tags: matched by label. Same duplicate policy as segments.
  • Environments: matched by name. Missing environments are created with settings from the export. For existing environments: SKIP leaves settings unchanged, OVERWRITE_DESTRUCTIVE updates settings to match the export.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions