Skip to content

Conversation

Copy link

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.

Co-authored-by: mrm9084 <1054559+mrm9084@users.noreply.github.com>
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 AI requested a review from mrm9084 June 17, 2025 18:39
Copilot finished work on behalf of mrm9084 June 17, 2025 18:39
Co-authored-by: mrm9084 <1054559+mrm9084@users.noreply.github.com>
Copilot AI requested a review from mrm9084 June 17, 2025 18:52
Copilot finished work on behalf of mrm9084 June 17, 2025 18:52
…nique list generation

Co-authored-by: mrm9084 <1054559+mrm9084@users.noreply.github.com>
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 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

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

mrm9084 and others added 3 commits June 19, 2025 12:56
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…cation

Co-authored-by: zhiyuanliang-ms <141655842+zhiyuanliang-ms@users.noreply.github.com>
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