Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IS-310: Setup GrowthBook for BE #926

Merged
merged 44 commits into from
Aug 30, 2023
Merged

Conversation

harishv7
Copy link
Contributor

@harishv7 harishv7 commented Aug 28, 2023

Problem

We are currently feature flagging using env variables which can get messy as we rollout more features. We also do not have an easy way to do things like percentage rollouts.

Closes IS-310

Solution

This PR introduces GrowthBook as a feature flagging system. It sets up some basic code that intialises the SDK and adds growthbook as an req property. Each time we do this, it creates a new instance of GrowthBook.

Note that growthbook can also be imported directly into the file/service where required too. The above req property injection helps us to perform conditional handling starting at controller level.

Breaking Changes

  • Yes - this PR contains breaking changes
    • Details ...
  • No - this PR is backwards compatible

Tests

-[ ] Create a feature flag on GrowthBook - either boolean or string or object
-[ ] Use the isOn or getFeatureValue SDK functions in req.growthbook object to retrieve the created flag
-[ ] Perform a conditional flow in one of the routes and see the behaviour

Deploy Notes

Please remove existing env var of WHITELISTED_GIT_SERVICE_REPOS from EB.

Deprecated environment variables: WHITELISTED_GIT_SERVICE_REPOS

New feature flags
ggs_whitelisted_repos: ensure correct values are set on GB portal

New environment variables:

  • GROWTHBOOK_CLIENT_KEY: required for SDK to connect to GrowthBook

New dependencies:

  • @growthbook/growthbook: GrowthBook SDK
  • cross-fetch: Polyfill for client side libs
  • eventsource: Polyfill for client side libs

Copy link
Contributor

@QiluXie QiluXie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm. Added a few nit, non-blocking comments.

src/types/request.ts Show resolved Hide resolved
src/utils/growthbook-utils.ts Show resolved Hide resolved
src/utils/growthbook-utils.ts Outdated Show resolved Hide resolved
src/types/request.ts Outdated Show resolved Hide resolved
@harishv7 harishv7 requested a review from QiluXie August 29, 2023 15:34
Copy link
Contributor

@QiluXie QiluXie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm. Thanks @harishv7 for addressing the comments quickly.

…-feature-flag-to-gb

IS-506: Migrate GGS Whitelist Feature Flag to GB
…-ld-for-segmentation

IS-312: Inject User Data into GrowthBook for Segmentation
Copy link
Contributor

@seaerchin seaerchin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my main problem with this approach is that suddenly alot of middleware ignorant stuff suddenly has to become middleware-aware

this leads to our codebase being resistant to change - if for every new feature, i'd have to pass sessionData down 3-4 layers, it gets very untenable very quickly. this is already felt most clearly when we have a nested router, and i don't want to make the problem worse than it already is.

given that this is already approved, i don't have an issue with it functionality-wise. maintainability-wise, i feel like the more direct approach of just having a global singleton might be the best - we don't actually need to pass it clientKey to getNewGrowthbookInstance as it's an env var + this allows us to trim away the deps passing of sessionData

.env-example Show resolved Hide resolved
src/classes/UserWithSiteSessionData.ts Show resolved Hide resolved
.loadFeatures({ autoRefresh: true })
.then(() => next())
.catch((e: unknown) => {
console.error("Failed to load features from GrowthBook", e)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might benefit from using logger here because we want to actually debug cases in prod, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

@harishv7
Copy link
Contributor Author

harishv7 commented Aug 30, 2023

my main problem with this approach is that suddenly alot of middleware ignorant stuff suddenly has to become middleware-aware

this leads to our codebase being resistant to change - if for every new feature, i'd have to pass sessionData down 3-4 layers, it gets very untenable very quickly. this is already felt most clearly when we have a nested router, and i don't want to make the problem worse than it already is.

given that this is already approved, i don't have an issue with it functionality-wise. maintainability-wise, i feel like the more direct approach of just having a global singleton might be the best - we don't actually need to pass it clientKey to getNewGrowthbookInstance as it's an env var + this allows us to trim away the deps passing of sessionData

@seaerchin Isn't sessionData already being passed down? It's analogous to the context of a request which is short-lived till that operation finishes

just having a global singleton might be the best

The SDK is the singleton, every time we do a new GrowthBook(), behind the scenes it is actually using a singleton.

We need to instantiate a new "wrapper" over this singleton for every request since it belongs to the session of the user. It should be tied to the user's request. Hence, think of it as being part of the session

@harishv7
Copy link
Contributor Author

my main problem with this approach is that suddenly alot of middleware ignorant stuff suddenly has to become middleware-aware
this leads to our codebase being resistant to change - if for every new feature, i'd have to pass sessionData down 3-4 layers, it gets very untenable very quickly. this is already felt most clearly when we have a nested router, and i don't want to make the problem worse than it already is.
given that this is already approved, i don't have an issue with it functionality-wise. maintainability-wise, i feel like the more direct approach of just having a global singleton might be the best - we don't actually need to pass it clientKey to getNewGrowthbookInstance as it's an env var + this allows us to trim away the deps passing of sessionData

@seaerchin Isn't sessionData already being passed down? It's analogous to the context of a request which is short-lived till that operation finishes

just having a global singleton might be the best

The SDK is the singleton, every time we do a new GrowthBook(), behind the scenes it is actually using a singleton

I clarified the part on singleton with GB team:

"Our JS and React SDKs create a shared, cached singleton across all SDK instances and each new SDK instance is just a thin, short-lived wrapper around that singleton. This allows each SDK instance to be user-specific (based on attributes) while still tapping into the central feature repository."

@seaerchin
Copy link
Contributor

@seaerchin Isn't sessionData already being passed down? It's analogous to the context of a request which is short-lived till that operation finishes

tbh context is ok but the stuff that we're passing sessionData too seems too deeply nested to be aware of context (session data here). it should only be that our topmost layer (routes) are aware of the context, extract necessary values and passes them on. if we can either 1. make growthbook standalone or 2. extract required stuff from growthbook, i'm ok

The SDK is the singleton, every time we do a new GrowthBook(), behind the scenes it is actually using a singleton.

We need to instantiate a new "wrapper" over this singleton for every request since it belongs to the session of the user. It should be tied to the user's request. Hence, think of it as being part of the session

yep, i think growthbook is structured as class instance (pass properties to segment) -> acc instance

@seaerchin
Copy link
Contributor

fyi, got build errors - might wanan fix prior to merge

@harishv7
Copy link
Contributor Author

fyi, got build errors - might wanan fix prior to merge

fixed

@harishv7
Copy link
Contributor Author

@seaerchin refactored methods to have finer depedencies

@harishv7 harishv7 merged commit 68d6508 into develop Aug 30, 2023
@mergify mergify bot deleted the IS-310-setup-base-for-be-code branch August 30, 2023 09:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants