Skip to content

Implement "last feature flag wins" behavior for duplicate feature flag IDs #59

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: main
Choose a base branch
from

Conversation

Copilot
Copy link

@Copilot Copilot AI commented Jun 17, 2025

This PR implements the "last feature flag wins" behavior when multiple feature flags have the same ID in the configuration, aligning with the Microsoft Feature Management schema used in .NET and JavaScript implementations.

Problem

Previously, when multiple feature flags had the same id in the configuration, the first one encountered would be used. This behavior was inconsistent with other Microsoft Feature Management implementations where the last matching feature flag should win.

Solution

Modified the feature flag resolution logic to:

  1. Return the last matching feature flag instead of the first when multiple flags share the same ID
  2. Return unique feature flag names in the list operation to avoid duplicates

Changes Made

Core Implementation

  • _get_feature_flag(): Modified to iterate through all feature flags and return the last matching one
  • _list_feature_flag_names(): Updated to return only unique feature flag names while preserving order

Example Behavior

feature_flags = {
    "feature_management": {
        "feature_flags": [
            {"id": "MyFlag", "enabled": "false"},
            {"id": "MyFlag", "enabled": "true"},   # This one wins
        ]
    }
}

feature_manager = FeatureManager(feature_flags)
assert feature_manager.is_enabled("MyFlag") == True  # Last flag wins
assert feature_manager.list_feature_flag_names() == ["MyFlag"]  # No duplicates

Testing

  • Added comprehensive tests for duplicate feature flag scenarios
  • Added tests for mixed unique/duplicate flag configurations
  • Added corresponding async tests to ensure consistency
  • All existing tests continue to pass (no regression)

References

Fixes #52.


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

@Copilot Copilot AI changed the title [WIP] Last feature flag matches the name should win Implement "last feature flag wins" behavior for duplicate feature flag IDs Jun 17, 2025
@Copilot Copilot AI requested a review from mrm9084 June 17, 2025 18:39
Copilot finished work on behalf of mrm9084 June 17, 2025 18:39
@Copilot Copilot AI requested a review from mrm9084 June 17, 2025 18:52
Copilot finished work on behalf of mrm9084 June 17, 2025 18:52
@Copilot Copilot AI requested a review from mrm9084 June 17, 2025 19:04
Copilot finished work on behalf of mrm9084 June 17, 2025 19:04
@mrm9084 mrm9084 marked this pull request as ready for review June 19, 2025 19:53
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

Implements “last feature flag wins” resolution and deduplicated flag name listing to align with Microsoft Feature Management behavior.

  • Core feature resolution now returns the last matching flag instead of the first.
  • Flag name listing deduplicates IDs while preserving original order.
  • Added sync and async tests covering duplicate-flag scenarios.

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
featuremanagement/_featuremanagerbase.py Updated _get_feature_flag to reverse-search flags; dedupe names in _list_feature_flag_names.
tests/test_feature_manager.py Added sync tests for duplicate-flag handling and mixed scenarios.
tests/test_feature_manager_async.py Added async tests for duplicate-flag handling (last-wins behavior).
Comments suppressed due to low confidence (2)

tests/test_feature_manager.py:274

  • [nitpick] If preserving order is important, you might assert exact list equality (flag_names == expected_names) instead of comparing sets to ensure the intended ordering is tested.
        assert set(flag_names) == set(expected_names)

tests/test_feature_manager_async.py:222

  • Consider adding a test_duplicate_feature_flags_mixed_with_unique_async async test to mirror the sync mixed-flag scenario and ensure consistency across both suites.
    @pytest.mark.asyncio

Comment on lines 44 to 49
index = len(feature_flags) - 1

while index >= 0:
if feature_flags[index].get("id") == feature_flag_name:
return FeatureFlag.convert_from_json(feature_flags[index])
index -= 1
Copy link
Preview

Copilot AI Jun 19, 2025

Choose a reason for hiding this comment

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

[nitpick] Consider replacing the manual index-based loop with Python’s reversed for better readability. For example:

for feature_flag in reversed(feature_flags):
    if feature_flag.get("id") == feature_flag_name:
        return FeatureFlag.convert_from_json(feature_flag)
Suggested change
index = len(feature_flags) - 1
while index >= 0:
if feature_flags[index].get("id") == feature_flag_name:
return FeatureFlag.convert_from_json(feature_flags[index])
index -= 1
for feature_flag in reversed(feature_flags):
if feature_flag.get("id") == feature_flag_name:
return FeatureFlag.convert_from_json(feature_flag)

Copilot uses AI. Check for mistakes.

Copy link
Contributor

@mrm9084 mrm9084 Jun 19, 2025

Choose a reason for hiding this comment

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

I'm not sure if it maters given the data sizes of these. But going through backwards with the index will always be faster than calling reversed every time, then looking backwards. Thoughts?

@copilot

@Copilot Copilot AI requested a review from zhiyuanliang-ms June 20, 2025 05:16
Copilot finished work on behalf of zhiyuanliang-ms June 20, 2025 05:16
Copy link
Contributor

@mrm9084 mrm9084 left a comment

Choose a reason for hiding this comment

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

Looks good to me now, but needs another approval as this is basically my change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Last feature flag matches the name should win
3 participants