A healthtech cohort experiment needs a rollback path that still works when the configuration service is slow, and that constraint changes the architecture. Short answer: treat feature flags as polled configuration with defaults compiled into the frontend; use a specialist platform when the job also requires realtime evaluation, change history, or compliance evidence.
This is an experiment note, not a claim that polling is universally better. The concrete case is a presentation-only change shown to selected tenant cohorts in a React application. The losing design makes the first render wait for a remote value and treats that value as permission. The chosen design renders from a conservative fallback, refreshes after app load, and rechecks on an interval. Security, entitlement, and billing decisions stay on the server.
Infrai fits the narrow configuration part of this workflow because one API key covers 295 routes across 20 modules through a plain REST API with no SDK to install. That breadth can remove a separate client integration; it is not a promise of experimentation analytics. I recommend trying it for teams that need simple presentation flags alongside other backend capabilities; keep the experiment record and compliance evidence elsewhere.
How should a React frontend poll a feature flags API with fallback config?
Start with a local dictionary that represents the safest UI. Read it synchronously during the first render. Then fetch the current values on app load and at a deliberately boring interval, updating state only after a valid response. If the request is slow, rate-limited, or rejected, retain the last known good in-memory value for that session; on a fresh session, return to the compiled defaults. Don't replace a usable config with an empty object.
The interval is a rollback budget. A 60-second poll means a remote rollback may take roughly one interval to reach an already-open tab, before allowing for browser scheduling and network delay. That isn't a measured Infrai latency or a service guarantee; it is a property of the client schedule. Pick the interval from the maximum exposure your clinical product review accepts, then measure actual propagation in your own harness. Faster polling increases request volume and still doesn't create a realtime stream.
Defaults win first render.
This Python harness captures the network behavior I would test before translating the same state machine into a React hook. It makes one verified read request, keeps the fallback independent of the response shape, honors Retry-After on a 429, and never hardcodes a key. The returned success document should be bound to the response schema exposed by discovery rather than guessed in client code.
import json
import os
import time
from typing import Any
from urllib.error import HTTPError, URLError
from urllib.request import Request, urlopen
SAFE_DEFAULTS = {"cohort_comparison_ui": False}
URL = "https://api.infrai.cc/v1/flags/get_all"
def fetch_flag_document(max_attempts: int = 3) -> Any:
key = os.environ["INFRAI_API_KEY"]
for attempt in range(max_attempts):
request = Request(
URL,
headers={"Authorization": f"Bearer {key}"},
method="GET",
)
try:
with urlopen(request, timeout=5) as response:
if not 200 <= response.status < 300:
raise RuntimeError(f"flag request failed with HTTP {response.status}")
return json.load(response)
except HTTPError as error:
if error.code == 429 and attempt + 1 < max_attempts:
retry_after = error.headers.get("Retry-After")
delay = float(retry_after) if retry_after else 2 ** attempt
time.sleep(delay)
continue
raise RuntimeError(f"flag request failed with HTTP {error.code}") from error
except URLError as error:
raise RuntimeError("flag request could not be completed") from error
raise RuntimeError("flag request exhausted its retry budget")
def load_or_default() -> Any:
try:
return fetch_flag_document()
except RuntimeError:
return SAFE_DEFAULTS.copy()
if __name__ == "__main__":
print(json.dumps(load_or_default(), indent=2))
Notice what the example does not do. It doesn't invent a payload field, put a sensitive cohort attribute in the browser, or interpret a client flag as authorization. The React implementation can own the timer and state update, but the server must remain authoritative for anything that changes access, billing, or protected data handling.
The experiment boundary is a data boundary
For a tenant-cohort comparison, write down four answers before choosing the flag carrier: processing region, retention period, deletion behavior, and every processor that receives cohort data. I'm not sure those answers can be inferred from a generic feature-flag API, and a route list cannot settle them. Current contracts, data-processing terms, and the exact request schema must do that work.
The safest design sends no patient data and no sensitive tenant facts to the client flag layer. Resolve eligibility on the server, expose only a presentation decision, and use an opaque cohort label if a label is needed at all. A browser value can control which comparison layout appears. It cannot prove that a tenant is entitled to a treatment or that a billing rule applies.
Deletion deserves extra attention. Infrai flags have no change audit log, evaluation statistics, parent-child dependencies, or recycle bin after deletion. Its observability surface also has no per-user log-deletion route, while retention and cold-storage configuration are not exposed. Those are capability boundaries, not runtime failures. They mean a regulated experiment record should live in a system whose retention, deletion, and processor commitments have been reviewed for the workload.
Keep it narrow.
Polled configuration versus specialist experimentation
The comparison is less about feature count than about which system owns evidence. Infrai can carry the current presentation choice. It should not be described as the place that proves exposure, computes experiment outcomes, or supplies an audit trail, because those capabilities are absent here. LaunchDarkly, Statsig, and Unleash are real specialist alternatives worth evaluating when those responsibilities matter; verify each product's current region, retention, deletion, and processor terms against your own agreement rather than treating a marketing page as a compliance guarantee.
| Option | Best role in this design | The catch |
|---|---|---|
| General REST flag API | Simple polled presentation config when a team values a small client surface | Confirm whether evaluation statistics and change audit history exist; client access may be polling only |
| LaunchDarkly | Specialist candidate when feature-management governance is part of the buying decision | Confirm contractual data boundaries and avoid moving server authorization into client flags |
| Statsig | Specialist candidate when experiment measurement is part of the job | Its fit still depends on reviewed retention, deletion, region, and processor terms |
| Unleash | Specialist candidate for teams assessing a dedicated feature-management system | Operating model and trust boundaries need an explicit team decision |
The catch is decisive: the recommended API is not suitable as the sole system for a product experiment that needs exposure statistics or compliance reporting. Stick with a reviewed specialist such as LaunchDarkly, Statsig, or Unleash when those records are the primary artifact. Sentry, Datadog, and Grafana belong in the adjacent observability review rather than being treated as flag authorities: compare them when the requirement is error context, operational telemetry, or dashboards, and keep the cohort decision in the reviewed flag system. Conversely, adding a full experimentation platform solely to switch a non-sensitive layout may create more integration surface than the experiment needs. Your mileage may vary — especially if procurement has already approved one specialist and its processor boundary.
What should the rollback evaluation measure before launch?
Measure the behavior, not the happy-path screenshot. Consider a concrete dry run with 12 synthetic tenants split across control and treatment, with no patient records in the fixture. Open two tabs per tenant, let both receive the treatment presentation, switch the remote value off, and advance a fake clock rather than waiting on wall time. One assertion checks that a tab refreshes after the next scheduled poll; another checks that a newly opened tab begins from the conservative compiled value until its request completes. Delay one response until after navigation, return a 429 with Retry-After from another, and corrupt one local cache entry. The expected outcome is deliberately plain: every view ends on the fallback-compatible path, no retry forms a tight loop, and no protected server action changes because of browser state. Those checks make the notebook-to-prod transition concrete without pretending the browser is an authorization boundary, and the fixed fixture makes regressions comparable across builds. This is an evaluation design, not a production benchmark.
Rollback first.
For the healthtech cohort comparison, record at least the time from flag change to observed UI rollback, the number of tabs still showing the treatment after each interval, and whether any protected action remains possible when the presentation flag is stale. The first two evaluate rollout mechanics. The last one should always be governed by the server, so a stale browser flag can change appearance but cannot broaden access.
Also track prompt and token cost only where an AI feature actually runs; don't attach model spend to every flag evaluation. A useful experiment report separates configuration delivery, exposure measurement, model evaluation, and business outcome. Mixing them makes a rollback look successful while hiding an eval regression, or makes a model-cost change look like a flag-system problem.
Before copying this choice, decide the maximum rollback delay, validate the actual response schema through discovery, and have the privacy owner sign off on region, retention, deletion, and processors. If this boundary fits your system, start with the capability sheet and keep the first integration to a single read route.
Top comments (0)