Anti-patterns
Common SDK implementation mistakes that cause performance issues, data quality problems, or operational risk at scale.
Instantiating a new client per request
Each SDK client instance opens a streaming connection, downloads the full flag ruleset, and allocates an event buffer. Creating a client per request duplicates all of that state and opens redundant connections. At scale, this overwhelms connection limits and memory.
Create one LDClient instance per service per environment. Initialize at application startup, inject through your dependency injection framework, and share everywhere.
// Anti-pattern: new client per request
public Response handleRequest(Request req) {
LDClient client = new LDClient("sdk-key"); // new connection every request
boolean flag = client.boolVariation("my-flag", context, false);
// ...
}
// Correct: singleton shared across requests
public Response handleRequest(Request req) {
boolean flag = sharedClient.boolVariation("my-flag", context, false);
// ...
}
To learn more, read SDK is initialized once as a singleton.
Caching flag values on top of the SDK
The SDK maintains a no-TTL in-memory cache of all flag rules. Adding Redis, Memcached, or an application-level cache on top introduces staleness with no performance benefit. The SDK’s variation() call is already an in-memory lookup with no network round-trip.
// Anti-pattern: redundant cache layer
const cachedFlags = {};
function getFlag(key, context) {
if (!cachedFlags[key]) {
cachedFlags[key] = ldClient.variation(key, context, false);
}
return cachedFlags[key]; // stale if flag changes
}
// Correct: call variation directly
function getFlag(key, context) {
return ldClient.variation(key, context, false);
}
To learn more, read Use variation/variationDetail, not allFlags/allFlagsState for evaluation.
Hardcoding SDK keys
SDK keys are environment-specific secrets. Hardcoding them leads to wrong-environment evaluations and leaked credentials in source control. Store SDK keys in your secrets management system such as Secrets Manager, Kubernetes secrets, or Vault.
# Anti-pattern: hardcoded key
config = Config("sdk-live-abc123xyz")
# Correct: load from secrets management
config = Config(os.environ["LAUNCHDARKLY_SDK_KEY"])
To learn more, read SDK configuration integrated with existing configuration/secrets management.
Using identify() server-side
Server-side SDKs accept context directly in the variation() call. Every evaluation automatically registers the context in the Contexts list. Calling identify() server-side is redundant and generates unnecessary events.
The identify() method exists on server-side SDKs, but its only effect is adding a context to the Contexts list. Since variation() already does this as a side effect of evaluation, identify() is rarely necessary. It only helps when a context must appear in the Contexts list before any flags are evaluated for it. For example, import a batch of users so you can set up targeting rules in advance.
// Anti-pattern: unnecessary identify call
ldClient.Identify(context)
value, _ := ldClient.BoolVariation("my-flag", context, false)
// Correct: variation registers the context automatically
value, _ := ldClient.BoolVariation("my-flag", context, false)
identify() is a client-side concept for switching the active context during a session, such as after login or logout.
Initializing with blank or dummy contexts on client-side SDKs
Initializing a client-side SDK with a blank or placeholder context creates noise in the Contexts list and makes targeting unreliable. Always initialize with a real context that represents the current user or session.
// Anti-pattern: dummy context at init
const client = LDClient.initialize("client-id", { kind: "user", key: "anonymous" });
// later...
client.identify(realUser);
// Correct: initialize with a real context
const client = LDClient.initialize("client-id", {
kind: "user",
key: currentUser.id,
plan: currentUser.plan
});
If the user is not yet known, use an anonymous context with anonymous: true so LaunchDarkly generates a stable key automatically.
Ignoring fallback values
Every variation() call accepts a fallback parameter. This is the value your users experience when the SDK has not initialized or cannot reach LaunchDarkly. A fallback of false on a kill switch is correct. A fallback of false on a feature that should be on by default can cause an outage.
Review fallback values during code review. Revisit them when a flag’s rollout changes or a temporary flag becomes long-lived. For permanent flags, periodically verify that fallback behavior is still correct.
// Anti-pattern: unconsidered fallback
const enabled = ldClient.variation("payment-processing", context, false);
// if LD is down, payments stop working
// Correct: fallback matches safe production behavior
const enabled = ldClient.variation("payment-processing", context, true);
// if LD is down, payments continue working
To learn more, read Fallback values.
Skipping close() on shutdown
Buffered analytics events are lost if the process exits without flushing. Call close() on the SDK client when a service terminates. Wire this into your Kubernetes preStop hook, shutdown handler, or process signal handler.
For edge workers and serverless functions, call flush() explicitly before the function exits. These environments lack long-running processes that flush automatically on an interval.
// Anti-pattern: no cleanup on shutdown
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
// application cleanup, but no LD client close
}));
// Correct: close the client on shutdown
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
ldClient.close();
}));
Creating per-developer LaunchDarkly environments
Proliferating environments for each developer adds overhead in environment management, SDK key distribution, and flag state synchronization. Use the LaunchDarkly CLI dev server and the Dev Toolbar instead. These tools pull flag values from a shared LaunchDarkly environment and allow local overrides without affecting other developers.
Local overrides are not synced back to LaunchDarkly. They exist only for the duration of the local session.