Invalidate a server cache the instant a config flips
Use onChange to drop a memoized render or a derived config on the next poll, so a dashboard change takes effect without a restart.
You memoize an expensive render — a navigation tree, a pricing table, a derived config object — and key it off a flag or dynamic config. It's fast, but now it's stale: someone flips the config in the dashboard and your long-lived server keeps serving the cached version until the next deploy. Flag reads are fresh on every poll; your derived cache isn't.
onChange closes the gap: it fires the moment a background poll picks up new rules, so you can drop the derived cache exactly when — and only when — something actually changed.
import { Engine } from "@shipeasy/sdk/server";
const engine = new Engine({ apiKey: process.env.SHIPEASY_SERVER_KEY! });
await engine.init();
const unsubscribe = engine.onChange(() => {
rebuildRenderCache(); // drop the memoized render / derived config
});
// on shutdown
unsubscribe();The poll carries an ETag. An unchanged body comes back 304 and listeners
stay quiet — you only wake up when the dashboard actually changed. It never fires in test or
offline mode, since no polling happens there.
In the browser the equivalent is engine.subscribe(), which fires after each identify() and after any override change — exactly when a re-render is warranted:
import { Engine } from "@shipeasy/sdk/client";
const engine = new Engine({ clientKey: process.env.NEXT_PUBLIC_SHIPEASY_CLIENT_KEY! });
const unsubscribe = engine.subscribe(() => {
renderFlags();
});Rollout & measurement plan
- Invalidate, don't poll your cache. Don't re-derive on every request "just in case" — subscribe once and rebuild only on the change event.
- Latency is the poll interval. A config flip lands within one poll (30s on Free, 60s on Team), not instantly — that's the freshness contract, and it's plenty for cache busting.
- Serverless has nothing to subscribe to. A one-shot request reads the latest blob and exits;
onChangeis for long-lived servers, background jobs, and live dashboards.
Debug why a user isn't seeing a feature
Use getFlagDetail reasons to tell DEFAULT (rolled to 0%) from FLAG_NOT_FOUND (typo or stale bundle) from OFF (killed).
Deterministic flags in CI and unit tests
Use forTesting() + overrides for unit tests and a captured snapshot for integration and CI — flag-gated code that never touches the network.