Skip to main content

Overview

NuGet.Services.FeatureFlags is the core library for runtime feature control across NuGet.org services. It separates flag evaluation from flag persistence, providing a thin client that reads from an in-memory cache while a background refresh loop keeps the cache synchronized with the authoritative state stored externally (in practice, a JSON file in Azure Blob Storage). This means flag checks are always synchronous and allocation-free at call time — there are no async waits or I/O on the hot path. The library distinguishes between two kinds of toggles. Features are binary on/off switches applicable to the entire system — for example, disabling package uploads when ingestion is degraded. Flights are targeted rollouts that can be enabled for a defined subset of users identified by username, email domain, or site-admin status, or for all users at once. This makes it possible to gradually expose new behavior to administrators or early-access testers without shipping separate code paths for each audience. The project targets both net472 and netstandard2.0, keeping it compatible with the legacy ASP.NET MVC gallery and with modern .NET Standard-based jobs and validation services. Its only external dependency is Microsoft.Extensions.Logging, which it uses exclusively for diagnostic output when flags are missing or the cache has not yet been loaded.

Role in System

The storage layer is intentionally not part of this library — IFeatureFlagStorageService is the only extension point it exposes. The gallery implements FeatureFlagFileStorageService (read-only) and EditableFeatureFlagFileStorageService (adds ETag-based save and user-removal) in NuGetGallery.Core. Jobs that consume flags register their own storage implementations.

In-Memory Cache

All flag state is held in a single FeatureFlags object stored in a private field on FeatureFlagCacheService. Reads are lock-free field reads; writes replace the field atomically. No per-request I/O.

Targeted Flights

Flight supports four targeting axes: all users, site admins, a list of explicit usernames, and a list of email domains. All comparisons are case-insensitive. The All property short-circuits every other check when true.

Graceful Degradation

When the cache is empty or a flag name is unknown, IsEnabled returns the caller-supplied defaultValue rather than throwing, and emits a structured log entry at LogError or LogWarning level for observability.

Staleness Telemetry

After each refresh attempt, FeatureFlagCacheService reports the elapsed time since the last successful load to the optional IFeatureFlagTelemetryService. If flags have never loaded, it reports TimeSpan.MaxValue.

Key Files and Classes

Dependencies

NuGet Package References

Internal Project References

This project has no internal project references. It is consumed by other projects in the solution.

Notable Patterns and Implementation Details

FeatureFlagCacheService accepts a Func<IFeatureFlagStorageService> factory rather than a direct IFeatureFlagStorageService instance. This allows the DI container (Autofac, in the gallery) to resolve a new storage instance on each refresh cycle — important when the storage service itself holds scoped resources such as HTTP clients or Azure SDK clients.
RefreshAsync is intended to be called once at application startup before the background loop begins, ensuring that all flag checks after initialization have a valid cache. The interface’s XML doc explicitly calls this out: “This should be called at app startup to guarantee feature flags have been loaded.”
The IsEnabled(string flight, IFlightUser user, bool defaultValue) overload does not gate anonymous access automatically. Passing null for user and having Flight.All = true will return true for an anonymous caller. Code that should never enable a flight for logged-out users must check authentication state before calling this API — the library’s own documentation calls this out explicitly.
All feature and flight name lookups use OrdinalIgnoreCase dictionaries constructed at FeatureFlags creation time. This means the FeatureFlagClient performs a single dictionary lookup per call with no string allocation for case normalization.
The IFeatureFlagTelemetryService parameter in the longer FeatureFlagCacheService constructor is optional by convention — a separate constructor overload omits it and passes null internally. Callers that do not need staleness metrics can use the shorter constructor without registering a telemetry service.
Feature flag state is serialized as a JSON file in Azure Blob Storage and managed through the NuGet.org admin panel. The NuGetGallery.Core project provides EditableFeatureFlagFileStorageService, which adds ETag-based optimistic concurrency for saves and a RemoveUserAsync method for GDPR-style user data removal from flight allowlists.