Hi! Our team is using GrowthBook together with Amp...
# experimentation
h
Hi! Our team is using GrowthBook together with Amplitude. I’m a client-side developer and I’d like to clarify one point.
Copy code
export const gbInstance = new GrowthBook({
  apiHost: import.meta.env.VUE_APP_GROWTHBOOK_API_HOST,
  clientKey: import.meta.env.VUE_APP_GROWTHBOOK_CLIENT_KEY,
  enableDevMode: !isProduction(),
  plugins: [autoAttributesPlugin()],
  trackingCallback: (experiment, result) => {
    $analytics.track({
      event_type: 'Experiment Viewed',
      event_properties: {
        experimentId: experiment.key,
        variationId: result.key
      }
    });
    console.log(`key-${experiment.key} result-${result.key}`);
  }
});
const initializeGrowthBook = async () => {
  try {
    if ($analytics) {
      gbInstance.updateAttributes({
        id: $analytics.amplitude.getUserId(),
        deviceId: $analytics.amplitude.getDeviceId()
      });
    }
    await gbInstance.init({ streaming: true });
    gbFlags.initialize(gbInstance);
    return gbInstance;
  } catch (e) {
    return null;
  }
};
I’m implementing it like this, and in the plugins I specify
autoAttributesPlugin()
. That generates attributes for targeting (one of them is
id
). When setting up an experiment in the admin panel, you can assign it based on
anonymous_id
or
user_id
. How is this
id
connected with
user_id
or
anonymous_id
? Or do I need to explicitly set
user_id
and
anonymous_id
when initializing? If so, how should this be connected with Amplitude, since it has both
user_id
and
device_id
? (edited)
s
Hey, hey! This part of things can be a bit confusing, but you're on the right path. The id in the SDK is what GrowthBook uses for targeting/hashing. It doesn't automatically map to user_id or anonymous_id in your data. For Amplitude, you can pass both identifiers into GrowthBook, and then choose which to bucket on per experiment. You can use the autoAttributesPlugin() as a backup or for additional attributes, but it's not necessary. Let me know if you have more questions.
h
@strong-mouse-55694 Please explain what the
user_id
and
anonymous_id
are based on (how they’re generated) and how to link them with Amplitude, because when configuring metrics here I can only choose these two attributes. Please explain it step by step, if possible, so I can have a better understanding—I’d be very grateful.
Сan i do it like this?
Copy code
const initializeGrowthBook = async () => {
  try {
    if ($analytics) {
      gbInstance.updateAttributes({
         user_id: $analytics?.amplitude.getUserId() || null,
         anonymous_id: $analytics?.amplitude.getDeviceId() || null
      });
    }
    await gbInstance.init({ streaming: true });
    gbFlags.initialize(gbInstance);
    return gbInstance;
  } catch (e) {
    return null;
  }
}
first i will have user_id null for anonymous users. After login my user_id i will set equal to user id from amplitude
Copy code
gbFlags.updateAttributes({
              user_id: this.$analytics.amplitude.getUserId()
 });
after logout i will do this
Copy code
window.experiment.clear();
        window.amplitude.amplitude.setUserId(null);
        gbFlags.updateAttributes({
          user_id: window.amplitude.amplitude.getUserId()
   });
user_id here equal null
my user will always have anonymous_id and user_id after logging in
s
All the attributes are arbitrary and up to you. Generally, though, you'll have a logged-in user id and anonymous id. See https://docs.growthbook.io/event-trackers/amplitude You set up these attributes under Attributes in SDK Connections. Then, when creating an experiment, you'll choose one of those identifiers for bucketing. Further configuration options are available under your data source settings. With your code, you're right that you want to update the attributes with the Amplitude values. But the initialization script looks strange. I imagine you're setting up the gbInstance elsewhere? But you're not passing it into the function but then returning it. I'm also not sure what the gbFlags part is doing. Finally, I don't know what the window.experiment part is for. Is just a JS/TS app or are you using a framework?
h
Copy code
const featureCache = reactive(
  Object.fromEntries(
    Object.keys(GROWTHBOOK_FEATURES).map((feature) => [feature, null])
  ) as Record<FeatureKey, boolean | string | null>
);

let gbInstance: GrowthBook | null = null;
export const gbFlags = {
  initialize(gb: GrowthBook) {
    gbInstance = gb;
    gbInstance.setRenderer(() => {
      Object.keys(featureCache).forEach((key) => {
        featureCache[key as FeatureKey] =
          gbInstance?.evalFeature(GROWTHBOOK_FEATURES[key as FeatureKey])
            ?.value ?? null;
      });
    });
  },

  async updateAttributes(attributes: Record<string, string>) {
    gbInstance?.updateAttributes(attributes);
  },
I have services function like this
s
Gotcha.