Feature Flags, Caching, and the Fine Art of Not Hammering Your API
Feature flags need to be fast. You don't want an HTTP request every time your application asks: is this thing turned on?. Let's look at what actually happens under the hood.
Microsoft.FeatureManagement: Let IConfiguration Do Its Thing
If you're using Microsoft.FeatureManagement, your feature definitions typically come from IConfiguration. A common setup looks something like:
{
"FeatureManagement": {
"NewFeature": true
}
}
The important bit is that IConfiguration isn't querying appsettings.json from disk every time you ask for a value. Configuration providers load their data into the configuration system. .NET watches for changes and reloads the configuration when the file changes. The configuration root exposes change notifications, and options infrastructure can react to those changes. So you've got:
appsettings.json
↓
Configuration Provider
↓
IConfiguration
↓
Microsoft.FeatureManagement
↓
Your application
No disk read per feature check. No network request. Maybe you add your own cache just to be safe, maybe not. Either way, nice and boring. And boring is good.
But What About Remote Feature Flags?
That's where things get more interesting. FeatureFlags.app gets feature definitions over HTTP, so we don't want every call to IsEnabled() to become:
Application → HTTP → FeatureFlags.app → HTTP response
Repeated thousands of times per second. That's where the caching comes in. The HttpFeatureFlagClient uses IMemoryCache to cache the complete set of feature definitions. The default expiration is 15 minutes, and that can be configured with FeatureFlags:CacheExpirationInMinutes. There's also an explicit ClearCache() method when you want to force a refresh. So the flow looks more like:
First request
Application → Cache miss → HTTP → FeatureFlags.app
↓
Cache
Next requests
Application → Cache → Feature definitions
That's considerably less exciting for your network stack. Which is the point.
The Trade-Off: Freshness vs. Performance
Caching is one thing developers love to argue about. With a 15-minute TTL, a change made in FeatureFlags.app may not immediately appear in every application instance. You're intentionally trading some freshness for better performance. You can lower the expiration if you need faster updates. Or clear the cache. And if you're running multiple instances, remember that IMemoryCache is local to each process.
How Other Services Do It
Some services use server-side SDKs to maintain the feature flag state and update it through streaming. Flag evaluations happen locally against the cached rules rather than making a network request for every evaluation. That gives you:
Vendor
↓ streaming updates
Local SDK cache
↓
Flag evaluation
Very fast, very resilient, and capable of continuing using the last known state if the service becomes temporarily unreachable. It's a great architecture. It's also a considerably more sophisticated - and costly - one.
Different Tools, Same Goal
Microsoft.FeatureManagement relies on .NET's configuration infrastructure. FeatureFlags.app uses an in-memory cache with a configurable TTL. Others maintain locally cached rulesets and push updates. Different implementations. Same fundamental goal.
Don't make your feature flag evaluation depend on a network round trip. Your application has enough exciting problems already.