Feature management
Why We Use Feature Management
Feature flags allow us to:
| Benefit | Examples |
|---|---|
| Release safely in small increments | Dark launches, A/B testing, soft rollouts |
| Reduce branching complexity | Keep all code in main, toggle at runtime |
| Target specific users/tenants | Beta testers, internal staff, selected clients |
| Gradually roll out features | Percentage rollouts, environment-based release |
| Decouple deployment from release | Deploy anytime, enable when ready |
Our solution supports cloud-managed defaults with in-app admin overrides, so authorised product/admin users can toggle features without Azure access.
Architecture Overview
Feature state is determined using layered precedence:
| Order | Source | Purpose |
|---|---|---|
| 1️⃣ Highest | DB Overrides | Admin toggles for environment/tenant, emergency disable |
| 2️⃣ Default | Azure App Configuration (or config files in dev) | Rules & rollout (percentage, targeting, time windows) |
| 3️⃣ Lowest | Code defaults | Fallback only when config missing |
A custom Composite Feature Definition Provider merges these sources so the app uses the standard IFeatureManager API.
Getting Started
Install the Package
dotnet add package Synetec.FeatureManagement
Register in Program.cs
var builder = WebApplication.CreateBuilder(args);
// Load Azure App Configuration when enabled
builder.AddCentralizedAppConfiguration(); // Provided by the package
// Add Feature Management
builder.Services.AddSynetecFeatureManagement();
Run the EF migration to create the FeatureOverrides table.
Defining a Feature Flag
Each new feature must have:
| Requirement | Description |
|---|---|
| Name | Unique identifier (PascalCase) |
| Owner | Person/squad accountable for removal |
| Definition | In App Config (or appsettings for dev) |
| Expiry/Cleanup Plan | When flag & old code will be removed |
Azure App Config definition example:
Key: .appconfig.featureflag/NewCheckout
Label: prod
Filters you may use:
AlwaysOnMicrosoft.TargetingMicrosoft.PercentageMicrosoft.TimeWindow
Using Feature Flags in Code
In C#
Inject IFeatureManager to provide the feature management capability in a controller, handler or service. You can also use the [FeatureGate] attribute to control usage.
if (await _featureManager.IsEnabledAsync("NewCheckout"))
{
// New behaviour
}
else
{
// Existing behaviour
}
Minimal API with Feature Gate
app.MapGet("/checkout", [FeatureGate("NewCheckout")] () =>
{
return Results.Ok("New Checkout!");
});
In Angular
Load from API:
this.http.get('/api/config')
.subscribe(cfg => this.flags = cfg.featureFlags);
Use in template:
<div *appFeature="'NewCheckout'">
<!-- New UI -->
</div>
Updating Feature State
| Scenario | How to Toggle |
|---|---|
| Normal Product Rollout | Update in Azure App Config |
| Emergency Disable | Use Admin UI → DB override |
| Local Developer Testing | appsettings.Development.json or UserSecrets |
| CI / Containers | Env vars |
Feature Lifecycle (Required Process)
1. Plan
- Add name, owner, target users, success criteria
- Create flag in App Config, set OFF
- Add removal date (default: within 2 sprints after launch)
2. Develop
- All new logic behind
IFeatureManagercheck - Keep existing behaviour as fallback
- Implement tests for ON and OFF states
3. Internal Release
- Enable only for dev/test users (Targeting)
- Validate functionality end-to-end
4. Limited/Beta Release
- Enable for selected tenants or groups
- Admin must be able to toggle via UI
5. Gradual Rollout
- Increase exposure progressively (1% → 25% → 50% → 100%)
- Monitor logs, error rate, feedback
6. Global Release
- Set flag to AlwaysOn
- Remove overrides if any
7. Cleanup (Mandatory)
Remove within 2 sprints after full release:
| Remove | Description |
|---|---|
| Legacy code path | The code wrapped in the OFF branch |
| Flag definition | Remove from App Config & config files |
| DB override rows | Delete obsolete overrides |
| Tests for old logic | Update to reflect single true path |
| Documentation | Close related ADR/ticket |
Flags must not accumulate — no “flag graveyards”.
Emergency Procedure
If a new feature causes issues in production:
- Admin disables via Admin UI → instant DB override
- If override fails → disable in Azure App Config
- If config unavailable → code fallback path still safe
Local Development Rules
We avoid relying on Azure during development.
Local precedence for feature values:
| Priority | Source |
|---|---|
| 1 | UserSecrets |
| 2 | appsettings.Development.json |
| 3 | Code default |
Optional: to test real App Config locally
dotnet user-secrets set UseAzureAppConfig true
Developer Checklist
Before merging a feature:
- Feature name, owner, and removal date defined
- Flag added to App Config (OFF)
- All new code behind feature flag
- Unit tests cover ON and OFF branches
- Admin UI can toggle (if needed)
Before full release:
- Rollout plan executed (Targeting/Percentage)
- Monitoring & success metrics reviewed
After full release:
- Remove code behind OFF path
- Remove flag from App Config & DB
- Remove tests for old logic
- Update docs/ticket closed
Reference Components (Provided by the Package)
The NuGet package includes:
- Composite Feature Definition Provider (merges config + overrides)
- EF override store & migration
- Admin API helpers
- Angular directive + config loader (optional if using Angular)
- QuickStart sample