How do client-side feature flags differ from server-side ones, and what breaks if you ignore the difference?
Show the full answer Hide the answer
What is being tested
Whether you account for distribution lag, unupgradable clients and offline behaviour.
The differences
1. Distribution lag. A flag changed on the server takes time to reach clients, which have cached values. "Instant rollback" is instant server-side and takes minutes client-side — and that difference matters most during an incident, which is exactly when you are relying on it.
2. Deployed versions cannot be upgraded. Mobile clients from a year ago are still running. A flag must behave sensibly for every version in the field, forever, so client flags accumulate rather than being cleaned up on a deployment.
3. Offline behaviour. A client with no connection must have a cached value and a safe default. A flag system that blocks rendering while fetching is a self-inflicted outage.
4. Exposure. Flag names and values are visible to anyone inspecting the client. Never use a client flag for anything security-relevant, and expect unreleased feature names to leak.
5. Consistency with the server. If the same flag is evaluated in both places and they disagree — because the client's cache is stale — the user sees an incoherent experience.
What breaks if you ignore it
- A rollback that does not take effect for minutes while an incident continues.
- An app that will not start without connectivity.
- Old app versions behaving unexpectedly because a flag was removed server-side and the default for unknown flags was never defined.
- Users flipping between variants across sessions or devices, because assignment was not stable — which also invalidates any experiment.
- Incoherent interfaces where server and client disagree.
The design that works
Evaluate on the server where possible and send resolved values to the client. Removes distribution lag, disagreement and exposure at once.
Bootstrap with the page or session, so no extra round trip is required.
Cache locally with a safe default, and never block rendering.
Stable assignment, hashed on a persistent user identifier.
Design for stale. Assume some clients hold old values for a while, and ensure both states are acceptable.