Toggling Features at Runtime: Database-Backed Feature Flags
Photo: Museums Victoria (BY 4.0), via Openverse
There's a moment everyone who seriously runs software knows: a new feature has been rolled out, everything looked good in the test environment, and then production reports that something's wrong. In that moment, only one question matters: how fast can I get it back out? If the answer is "a new deployment, in twenty minutes," that's simply too slow for a health product.
So this week is about something that's invisible from the outside and yet represents one of the most important safeguards in Xircuit: a feature flag system that lets us switch functionality on and off at runtime, without a single deployment being necessary. I'll explain why we built it, how it works technically, and what role it plays in cautiously rolling out new integrations.
Why a Deployment Isn't Enough as an Emergency Brake
The classic way to turn off a feature is a configuration value in appsettings.json, combined with a restart. That works, but it's slow and blunt. A deployment touches the entire service, takes its time, and needs to be well thought through. It doesn't work as an emergency brake, because an emergency brake has to be something you can pull while the train is moving.
We wanted to decouple the decision of whether a feature is active from the build and move it into running operations instead. A flag should be flippable like a physical switch on a control panel: instantly, visibly, and without anyone having to trigger a pipeline for it. That's not a luxury — it's a matter of operational safety, especially when background services are continuously polling external systems.
The Database as Truth, Configuration as a Safety Net
The core of the implementation is a deliberate split. Feature flags live in the database and can be toggled there at runtime. The static configuration from appsettings remains in place as a fallback. At first that sounds like double bookkeeping, but there's a concrete reason for it.
When the code asks whether a flag is active, the system first checks the database. If an entry exists there, it wins. If it's missing — say, because a flag is brand new or the table hasn't been populated yet on first startup — the configured default value kicks in. That way there's always a defined answer, even if the database happens to be unreachable, or a flag was simply forgotten. The database is the source of truth for what currently applies; the configuration is the safety net stretched underneath it.
In practice, that also means a flag has two lives. It's born with a safe default in code and can later be overridden while the system is running, without that default ever being lost. Reset the database entry, and behavior falls back to the configuration — a predictable baseline you can rely on.
Visible and Traceable: The Admin Page
A switch that only a developer knows about through a SQL statement isn't an emergency brake — it's a trap. That's why this system includes its own admin page at /platform/feature-flags. All flags are listed there, along with their current state, and can be toggled directly.
That accomplishes two things at once. First, toggling becomes visible: you don't have to guess which features are currently active, you see it at a glance. Second, it becomes auditable. Whoever changes something leaves a trail, and in an environment processing health data, that traceability isn't a nice-to-have, it's a requirement. Turning off a feature is an operational decision, and decisions like that should be documented, not vanish into a command line.
Photo: Tim Sullivan · StockSnap (CC0 1.0), via Openverse
Workers That Go Still Within Seconds
The most interesting part concerns the background workers. Xircuit runs a number of services that work away from any user interaction — they sync data, poll external interfaces, and process whatever comes in. These are exactly the kind of services you want to be able to shut down quickly if in doubt, say when a connected integration starts throwing errors or a provider throttles its API.
That's why the workers check the feature flags regularly and, on request, pause within at most thirty seconds. A worker doesn't run as a rigid infinite loop; between its passes it asks whether it should even keep working. If someone flips the corresponding flag on the admin page, the service stops at its next check interval, without crashing and without leaving work half-finished. It sleeps until the flag is released again.
Those thirty seconds are a deliberate trade-off. We could have the workers check on every single operation, but that would create unnecessary load on the database. A short, fixed check interval is fast enough to react in an emergency and frugal enough not to stand out during normal operation.
New Features Ship Turned Off, at First
For us, feature flags aren't just an emergency brake, they're also the default way of shipping risky things. New, sensitive features are brought into production behind an off flag and only activated after verification. The code is already sitting on the servers at that point, it's just not switched live yet.
That decouples two processes that would otherwise dangerously merge: shipping code and activating behavior. We can safely deploy an integration, verify it in the real environment against real dependencies, and only release it to users once we're confident it behaves correctly. If something goes wrong along the way, the way back is trivial — the flag goes back to off, and the state returns to whatever applied before the release. A "not yet verified, so it's behind a flag" is an honest and frequent statement in this dev log, and that's exactly what the mechanism is for.
The Foundation for Staged Rollouts
Beyond the individual on-off decision, the flags are also the foundation for something bigger: staged rollouts across the seven industries Xircuit serves. Not every new feature has to go live everywhere at once. A feature can be activated in one area first, observed there, and then extended step by step to the rest.
That turns a binary switch into a tool for controlled growth. Instead of rolling out every change as a big leap for everyone, it can be introduced in manageable stages, where each stage safeguards the next. That reduces the risk of every individual release and gives us the calm to observe things carefully before they go broad. The effort of building a system like this pays off precisely on the days when things would otherwise turn hectic.
What's Next?
Next week: the first health integration — continuous glucose monitoring with the FreeStyle Libre 3, and a key ring for encryption.
Comments
No comments yet. Be the first to share your thoughts!
Leave a Comment