EnumCraft.FeatureFlags 0.1.2-preview

This is a prerelease version of EnumCraft.FeatureFlags.
There is a newer version of this package available.
See the version list below for details.
dotnet add package EnumCraft.FeatureFlags --version 0.1.2-preview
                    
NuGet\Install-Package EnumCraft.FeatureFlags -Version 0.1.2-preview
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="EnumCraft.FeatureFlags" Version="0.1.2-preview" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="EnumCraft.FeatureFlags" Version="0.1.2-preview" />
                    
Directory.Packages.props
<PackageReference Include="EnumCraft.FeatureFlags" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add EnumCraft.FeatureFlags --version 0.1.2-preview
                    
#r "nuget: EnumCraft.FeatureFlags, 0.1.2-preview"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package EnumCraft.FeatureFlags@0.1.2-preview
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=EnumCraft.FeatureFlags&version=0.1.2-preview&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=EnumCraft.FeatureFlags&version=0.1.2-preview&prerelease
                    
Install as a Cake Tool

EnumCraft.FeatureFlags

Feature flag management system with tenant-specific overrides and auto-reload capabilities

NuGet License: MIT

What is EnumCraft.FeatureFlags?

A powerful feature flag system built on EnumCraft.Core that provides:

✅ Type-Safe Feature Flags - No magic strings, compile-time safety
✅ Tenant-Specific Overrides - Different settings per customer/tenant
✅ Three-Tier Priority System - Default → Global → Tenant
✅ Auto-Reload - Changes to config file automatically apply
✅ JSON Configuration - Simple, human-readable config files
✅ Resolution Transparency - See exactly where a flag's value comes from


Quick Start

Installation

dotnet add package EnumCraft.Core
dotnet add package EnumCraft.FeatureFlags

1. Define Your Feature Flags

using EnumCraft;
using EnumCraft.FeatureFlags;

public sealed partial class FeatureFlag : TypedEnumGuid<FeatureFlag>
{
    // Define your feature flags with Guids (stable across environments)
    public static readonly FeatureFlag DarkMode = 
        new(Guid.Parse("a1b2c3d4-e5f6-1234-5678-9abcdef01234"), 
            "Dark Mode UI", 
            nameof(DarkMode), 
            active: false);  // Default value
    
    public static readonly FeatureFlag AdvancedSearch = 
        new(Guid.Parse("b2c3d4e5-f612-3456-789a-bcdef0123456"), 
            "Advanced Search Features", 
            nameof(AdvancedSearch), 
            active: true);
}

2. Create Configuration File

Create FeatureFlags.json in your application directory:

{
  "global": {
    "DarkMode": true,
    "AdvancedSearch": true
  },
  "tenants": {
    "1001": {
      "DarkMode": false,
      "AdvancedSearch": true
    },
    "1002": {
      "DarkMode": true,
      "AdvancedSearch": false
    }
  }
}

3. Initialize at Application Startup

// In Global.asax.cs (ASP.NET) or Program.cs (.NET Core+)
FeatureFlag.Initialize();

// Optional: Configure custom path
// Add to web.config/app.config:
// <appSettings>
//   <add key="FeatureFlagDataPath" value="C:\Config\" />
// </appSettings>

4. Use Feature Flags

// Global (default) value
if (FeatureFlag.DarkMode.IsActive)
{
    EnableDarkMode();
}

// Tenant-specific value
int tenantId = GetCurrentTenantId();
if (FeatureFlag.DarkMode[tenantId])
{
    EnableDarkMode();
}

// String tenant ID also supported
string tenantCode = "ACME-CORP";
if (FeatureFlag.AdvancedSearch[tenantCode])
{
    ShowAdvancedSearch();
}

Priority System

Feature flags follow a three-tier priority system:

┌─────────────────────────┐
│  3. Tenant Override     │ ← Highest priority
│  (per-customer setting) │
└─────────────────────────┘
           ↓
┌─────────────────────────┐
│  2. Global Setting      │ ← Middle priority
│  (JSON configuration)   │
└─────────────────────────┘
           ↓
┌─────────────────────────┐
│  1. Default Value       │ ← Lowest priority
│  (constructor parameter)│
└─────────────────────────┘

Example

// Definition
public static readonly FeatureFlag Export = 
    new(guid, "Export Feature", nameof(Export), active: false);
//                                                      ^^^^^ Default: false

// FeatureFlags.json
{
  "global": {
    "Export": true  // Global override: true
  },
  "tenants": {
    "2001": {
      "Export": false  // Tenant 2001 override: false
    }
  }
}

// Results:
Export.IsActive;      // true  (global overrides default)
Export[0];            // true  (0 = global)
Export[1999];         // true  (tenant 1999 uses global)
Export[2001];         // false (tenant 2001 has specific override)

Auto-Reload

Feature flags automatically reload when FeatureFlags.json changes:

// Initialize with auto-reload enabled (default)
FeatureFlag.Initialize(enableAutoReload: true);

// Edit FeatureFlags.json while app is running
// Changes apply automatically within 2 seconds (no restart needed!)

How It Works

  • FileSystemWatcher monitors FeatureFlags.json for changes
  • Debounced reload (350ms) prevents thrashing during edits
  • Polling fallback (2s) ensures reliability if watcher fails
  • Thread-safe with proper locking

Resolution Transparency

Debug exactly where a flag's value comes from:

var resolutions = FeatureFlag.Export.Resolve(tenantId: 2001);

foreach (var r in resolutions)
{
    Console.WriteLine($"Source: {r.Source}, Priority: {r.Priority}, Value: {r.Value}");
}

// Output:
// Source: Tenant,  Priority: 1, Value: false   ← Used (highest priority)
// Source: Global,  Priority: 2, Value: true
// Source: Default, Priority: 3, Value: false

FeatureFlagResolution Properties

public readonly struct FeatureFlagResolution
{
    public string? FeatureFlag { get; }      // Flag description
    public int TenantID { get; }             // Tenant ID (0 = global)
    public FeatureFlagValueSource Source { get; } // Default/Global/Tenant
    public int Priority { get; }             // 1 = highest
    public bool Value { get; }               // The actual value
}

Configuration

AppSettings Configuration


<configuration>
  <appSettings>
    <add key="FeatureFlagDataPath" value="C:\Config\" />
  </appSettings>
</configuration>

If not configured, defaults to application base directory.

Initialize Options

// Basic initialization
FeatureFlag.Initialize();

// Custom file name
FeatureFlag.Initialize("MyFeatures.json");

// Disable auto-reload (manual reload only)
FeatureFlag.Initialize(enableAutoReload: false);

// Full customization
FeatureFlag.Initialize("CustomFeatures.json", enableAutoReload: true);

Custom Logging

Integrate with your logging framework:

using Microsoft.Extensions.Logging;

ILogger<FeatureFlag> logger = loggerFactory.CreateLogger<FeatureFlag>();
FeatureFlag.SetLogger(logger);

JSON Configuration Format

{
  "global": {
    "FeatureFlagCode1": true,
    "FeatureFlagCode2": false
  },
  "tenants": {
    "tenantId1": {
      "FeatureFlagCode1": false,
      "FeatureFlagCode3": true
    },
    "tenantId2": {
      "FeatureFlagCode2": true
    }
  }
}

Rules

  • Keys must match Code property from flag definitions
  • Tenant IDs can be strings or numbers (converted automatically)
  • Missing flags fall back to next priority level
  • Invalid JSON is ignored (logs error, uses previous valid config)

Advanced Usage

Implicit Bool Conversion

[Obsolete] // Marked obsolete - prefer explicit .IsActive
if (FeatureFlag.DarkMode)  // Implicitly converts to bool
{
    // Enabled
}

// Preferred:
if (FeatureFlag.DarkMode.IsActive)
{
    // Enabled
}

Cleanup on Shutdown

// In Application_End (ASP.NET) or on shutdown
FeatureFlag.DisposeWatcher();

Best Practices

✅ DO

  • Use Guids for IDs - stable across environments
  • Use descriptive names - "Advanced Search Features" not "Feature1"
  • Use nameof() for Code parameter - prevents typos
  • Initialize early - in Global.asax or Program.cs
  • Set default values thoughtfully - what's safest if config is missing?

❌ DON'T

  • Hard-code tenant IDs in code - pass them as parameters
  • Rely on auto-reload in critical paths - have sensible defaults
  • Edit JSON while saving - use atomic file operations (save temp, rename)

Framework Support

Framework Supported
.NET Framework 4.7.2+ ✅
.NET Standard 2.0+ ✅
.NET Core 3.1+ ✅
.NET 5+ ✅
.NET 6+ ✅
.NET 7+ ✅
.NET 8+ ✅

Dependencies

This package requires:

  • EnumCraft.Core - Base typed enumeration system
  • Microsoft.Extensions.Logging.Abstractions - Logging support
  • System.Configuration.ConfigurationManager - AppSettings access


Documentation

For comprehensive documentation and examples:


Contributing

Contributions are welcome! Please feel free to submit a Pull Request.


License

MIT License - see LICENSE file for details


Author

Randel Bjorkquist
GitHub | NuGet

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on EnumCraft.FeatureFlags:

Package Downloads
EnumCraft.FeatureFlags.Json

JSON file provider for EnumCraft.FeatureFlags.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.1 176 4/26/2026
1.0.0 158 4/26/2026
0.3.1-preview 154 4/22/2026
0.3.0-preview 150 4/20/2026
0.2.2-preview 155 4/6/2026
0.2.1-preview 165 3/2/2026
0.2.0-preview 167 2/24/2026
0.1.4-preview 124 2/22/2026
0.1.3-preview 125 2/19/2026
0.1.2-preview 121 2/17/2026
0.1.1-preview 127 2/16/2026
0.1.0-preview 128 2/16/2026