đŸ”„ Pragmatic .NET Code Rules Course is on Presale - 40% off!BUY NOW

Feature Flags without redeploying: what people usually get wrong in .NET

This issue is made possible thanks to JetBrains, who help keep this newsletter free for everyone. A huge shout-out to them for their support of our community. Let's thank them by entering the link below. Struggling with slow builds, tricky bugs, or hard-to-understand performance issues? dotUltimate fixes all of that. It’s the all-in-one toolbox for serious .NET developers. 👉 Upgrade your .NET workflow.

Introduction

How do you enable or disable features without redeploying the app?

If your first thought is “I’ll just flip a value in appsettings.json” - you’re not alone. But that’s also where most Feature Flag implementations quietly fail.

In the last few days, I shared a short post about Feature Flags in .NET, and the comments were exciting.

They all circled the same confusion:

Do we really avoid redeployment? How does this work with multiple instances? Is appsettings.json enough? Do I still need to run a DevOps release?

Let’s clear this up - with real code that actually works.

First: What “Without Redeploying” Actually Means

Let’s be very explicit.

“Without redeploying” means:

  • ❌ no build
  • ❌ no release pipeline
  • ❌ no app restart
  • ❌ no new container image
  • ✅ behavior changes while the app is running

    If changing a feature flag requires you to click 'Deploy', then you haven’t decoupled deployment from release - you've just moved the toggle.

    Why appsettings.json Is NOT Enough

Yes, you can define feature flags in appsettings.json.

C#
{ "FeatureManagement": { "BetaFeature": true }}

But here’s the problem:

  1. appsettings.json is read at startup
  2. each instance has its own copy
  3. any change requires:
    • file change
    • restart
    • redeploy So this works for:
    • local development
    • demos
    • small experiments

But it does not work for:

  • multiple instances
  • Kubernetes
  • Azure App Service
  • real production traffic

To truly avoid redeployment, feature flags must live outside the application.

The Correct Architecture

Feature Flags must be:

  • centrally stored
  • shared across instances
  • read at runtime
  • refreshable without a restart In .NET, this is exactly what Azure App Configuration + Feature Management gives us. Let’s build this properly.

Step 1: Install Required Packages

These give us:

  • Feature Flags API
  • Azure App Configuration integration
  • runtime refresh support
C#
dotnet add package Microsoft.FeatureManagement.AspNetCoredotnet add package Microsoft.Azure.AppConfiguration.AspNetCore

Step 2: Configure Azure App Configuration

In Azure Portal:

  1. Create Azure App Configuration
  2. Go to Feature Manager
  3. Create a feature flag:

Step 3: Add Connection String

In appsettings.json:

C#
{ "AzureAppConfig": { "ConnectionString": "<YOUR_CONNECTION_STRING>" }}

In real projects, this belongs in Key Vault or environment variables.

Step 4: Configure Program.cs

This is the part most examples skip or oversimplify.

C#
using Microsoft.FeatureManagement;using Microsoft.Extensions.Configuration.AzureAppConfiguration; var builder = WebApplication.CreateBuilder(args); // 1. Connect to Azure App Configurationbuilder.Host.ConfigureAppConfiguration(config =>{ var settings = config.Build(); config.AddAzureAppConfiguration(options => { options .Connect(settings["AzureAppConfig:ConnectionString"]) // Register feature flags .UseFeatureFlags(featureOptions => { featureOptions.CacheExpirationInterval = TimeSpan.FromSeconds(10); }) // Register refresh with a sentinel key .ConfigureRefresh(refresh => { refresh .Register("FeatureFlags:Sentinel", true) .SetCacheExpiration( TimeSpan.FromSeconds(10)); }); });}); // 2. Add Feature Managementbuilder.Services.AddFeatureManagement(); // 3. Add Azure App Config refresh middlewarebuilder.Services.AddAzureAppConfiguration(); var app = builder.Build(); // Enable refresh middlewareapp.UseAzureAppConfiguration(); app.MapGet("/", () => "Feature Flags Demo is running"); // Feature-gated endpointapp.MapGet("/beta", async (IFeatureManager featureManager) =>{ if (await featureManager.IsEnabledAsync("BetaFeature")) { return Results.Ok("Beta feature is ENABLED"); }  return Results.Ok("Beta feature is DISABLED");}); app.Run();

In Azure App Configuration:

Create a key-value pair:

  • Key: FeatureFlags:Sentinel
  • Value: any string (e.g. timestamp)

Whenever you update this value:

  • all feature flags refresh
  • no restart
  • no redeploy

Step 6: See It in Action

Run the app and call GET /beta → ❌ disabled Enable BetaFeature in the Azure Portal Wait ~10 seconds Call /beta again → 🚀 enabled

No redeploy. No restart. No pipeline.

Important Production Gotcha: Caching

Azure App Configuration does cache values.

If you don’t configure refresh:

  • changes may take minutes
  • developers think “it doesn’t work”

That’s why:

  • CacheExpirationInterval
  • sentinel keys
  • refresh middleware

matter a lot.

This is where most “Feature Flags don’t work” stories come from.

What About Multiple Instances?

This setup works perfectly with:

  • multiple app instances
  • load balancers
  • Kubernetes
  • App Service scaling Because:
  • flags are centralized
  • decision logic is deterministic
  • instances are stateless The feature decision does not depend on the instance.

3rd-Party Feature Flag Providers

Azure App Configuration is great if you’re already on Azure.

Other solid options:

  • LaunchDarkly
    • enterprise-grade
  • Unleash
    • open-source & self-hosted
  • Flagsmith
    • SaaS or self-hosted

The provider matters less than the runtime architecture.

When Feature Flags Become a Problem

Feature Flags are powerful - but dangerous if abused:

  • flags that live for years
  • nested if statements everywhere
  • “temporary” flags that never die Best practice:
  • flags should have an owner
  • flags should have a removal date
  • flags should not replace versioning

Wrapping Up

Feature Flags are not about hiding code paths.

hey are about decoupling deployment from release.

If flipping a flag requires a redeploy, you didn’t gain flexibility - you added complexity.

If you want a deeper dive, I’ve already written a full article on Feature Flags in .NET with Azure Feature Management here.

More real-world .NET architecture topics coming soon 🚀 That's all from me for today.


Want to enforce clean code automatically? My Pragmatic .NET Code Rules course shows you how to set up analyzers, CI quality gates, and architecture tests - a production-ready system that keeps your codebase clean without manual reviews. Or grab the free Starter Kit to try it out.

About the Author

Stefan Djokic is a Microsoft MVP and senior .NET engineer with extensive experience designing enterprise-grade systems and teaching architectural best practices.

There are 3 ways I can help you:

1

Pragmatic .NET Code Rules Course

Stop arguing about code style. In this course you get a production-proven setup with analyzers, CI quality gates, and architecture tests — the exact system I use in real projects. Join here.

Not sure yet? Grab the free Starter Kit — a drop-in setup with the essentials from Module 01.

2

Design Patterns Ebooks

Design Patterns that Deliver — Solve real problems with 5 battle-tested patterns (Builder, Decorator, Strategy, Adapter, Mediator) using practical, real-world examples. Trusted by 650+ developers.

Just getting started? Design Patterns Simplified covers 10 essential patterns in a beginner-friendly, 30-page guide for just $9.95.

3

Join 20,000+ subscribers

Every Monday morning, I share 1 actionable tip on C#, .NET & Architecture that you can use right away. Join here.

Join 20,000+ subscribers who mass-improve their .NET skills with actionable tips on C#, Software Architecture & Best Practices.

Subscribe to
TheCodeMan.net

Subscribe to the TheCodeMan.net and be among the 20,000+ subscribers gaining practical tips and resources to enhance your .NET expertise.