Skip to main content

Initializing SDKs

One of the first steps in using a Statsig client SDK is to call the asynchronous initialize() method in the SDK language of your choice. This retrieves the values you need to evaluate flags or experiments and send events: in client SDKs, only the events for your defined user are provided, while Server SDKs fetch your entire ruleset.

General Initialization Flow

initialize will take an SDK key and StatsigUser object, as well as a set of options to parameterize the SDK. The sdk will then do a few things:

  1. Check local storage for cached values. The SDK caches the previous evaluations locally so they are available on the next session without a successful network call
  2. Create a STATSIG_STABLE_ID - an ID that stays consistent per-device, which can often be helpful for logged-out experiments.
  3. Set the SDK as initialized so checks won't throw errors - they will either return cached values or defaults.
  4. Issue a network request to Statsig to get the latest values for all gates/experiments/configs/layers/autotunes for the given user. If the project definition does not change from the most recent cached values, this request may succeed without returning new data.
  5. Resolve the asynchronous initialize call. If the request to the server failed, the SDK will have the cached values or return defaults for this session.

Depending on when you check a gate or experiment after initializing, its possible that you may not have retrieved fresh values yet. Awaiting the return of the initialize function is one easy way to do this, but has speed disadvantages, discussed below.

Client Initialization Strategies

We now offer several strategies for initializing StatsigClient — allowing customers to fine-tune and minimize latency. The various strategies carry some tradeoffs, and should be carefully considered based on your performance requirements and experimentation needs.

Below are the various strategies summarized at a high level, ordered from most common to least common:

  • Asynchronous Initialization (Awaited): Ensures user assignments are available after initialization but adds latency due to awaiting fresh assignments being fetched over the network from Statsig servers.
  • Bootstrap Initialization: Best of both worlds for latency and availability of fresh assignments, but requires additional engineering effort. Pass up-to-date Statsig values down with other server responses, minimizing latency.
  • Asynchronous Initialization (Not Awaited): This ensures immediate rendering, but in a state that reflects stale assignments or no assignments available (resulting in default values being used).
    • After initialization, the client will then fetch fresh assignments over the network from Statsig. Subsequent calls to check assignments may result in different assignments than the initial state and therefore render a different treatment (this is referred to as "flicker"). This mimics the old behavior of StatsigProvider.waitForInitialization=false.
  • Synchronous Initialization: Ensures immediate rendering but with stale or no assignments available. First-visit users will never be assigned to gates and experiments. These users will only see updated assignments after they do a hard-refresh of the website. Effectively, all assignment information is 1 page load stale.

1. Asynchronous Initialization - Awaited

Ensures latest assignments but requires a loading state

When calling StatsigClient.initializeAsync, the client loads values from the cache and fetches the latest values from the network. This approach waits for the latest values before rendering, which means it is not immediate but ensures the values are up-to-date.

Example:

...

2. Bootstrap Initialization

Ensures both latest assignments with no rendering latency

Bootstrapping allows you to initialize the client with a JSON string. This approach ensures that values are immediately available without the client making any network requests. Note that you will be responsible for keeping these values up to date. With this approach, your server will be responsible for serving the configuration payload to your client app on page load (for web implementations) or during app launch (for mobile implementations).

This architecture requires running a server SDK that supports the getClientInitializeResponse method. Your server SDK will maintain a fresh configuration in memory and when a request hits your route handler, you should call getClientInitializeResponse(<user>), passing in a StatsigUser Object to generate the configuration object that gets passed to the client SDK for synchronous initialization.

Implementation Notes:

  • You should pass the same user object on both the server and client side - take care that these stay in sync. This can become particularly hairy in the case of keeping stableID in sync, as it'll re-generate when it doesn't exist. See here.
  • The initializeValues option should be an Object - except in our js SDK, where its expected to be a string. Calling .stringify() on the object should work.

Example:

...

3. Asynchronous Initialization - Not Awaited

If you want to fetch the latest values without awaiting the asynchronous call, you can call initializeAsync and catch the promise. This approach provides immediate rendering with cached values initially, which will update to the latest values mid-session.

caution

Be aware that the values may switch when checked a second time after the latest values have been loaded.

Example:

...

4. Synchronous Initialization

Ensures immediate rendering but uses cached assignments (when available)

When calling StatsigClient.initializeSync, the client uses cached values if they are available. The client fetches new values in the background and updates the cache. This approach provides immediate rendering, but the values might be stale or absent during the first session.

Example:

...

These strategies help you balance the need for the latest gate / experiment assignment information with the need for immediate application rendering based on your specific requirements.

/Initialize Response Schema

Provided for reference if you're implementing Bootstrapping - the job of your server is to provide the values that Statsig's servers other wise would when they call /initialize. Statsig's getClientInitializeResponse function provides this payload.

/** Specs for Dynamic Configs */
dynamic_configs: {
[key: string]: {
name: string;
rule_id: string | null;
value: { [key: string]: unknown };
group?: string;
secondary_exposures?: {
gate: string,
gateValue: string,
ruleID: string,
}[];
undelegated_secondary_exposures?: {
gate: string,
gateValue: string,
ruleID: string,
}[];
is_device_based?: boolean;
is_user_in_experiment?: boolean;
is_experiment_active?: boolean;
explicit_parameters?: string[];
is_in_layer?: boolean;
allocated_experiment_name?: string;
};
};
/** Specs for Feature Gates */
feature_gates: {
[key: string]: {
name: string;
value: boolean;
rule_id: string | null;
secondary_exposures?: {
gate: string,
gateValue: string,
ruleID: string,
}[];
};
};
/** Specs for Layer Configs */
layer_configs: {
[key: string]: {
name: string;
rule_id: string | null;
value: { [key: string]: unknown };
group?: string;
secondary_exposures?: {
gate: string,
gateValue: string,
ruleID: string,
}[];
undelegated_secondary_exposures?: {
gate: string,
gateValue: string,
ruleID: string,
}[];
is_device_based?: boolean;
is_user_in_experiment?: boolean;
is_experiment_active?: boolean;
explicit_parameters?: string[];
is_in_layer?: boolean;
allocated_experiment_name?: string;
};
};
/** Whether the response contains updates from the sinceTime */
has_updates: boolean;
/** Name of the service that generated the response */
generator: string;
/** Timestamp of response */
time: number;
/** Timestamp of company's last config update time */
company_lcut: number;
/** The user keys evaluated */
evaluated_keys: {
userID?: string;
stableID?: string;
customIDs?: Record<string, string>;
};
/** The hashing algorithm used */
hash_used: string;