-
Notifications
You must be signed in to change notification settings - Fork 0
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
chore: move initialization into class methods, limit singleton use #170
Conversation
@@ -287,308 +584,42 @@ export function buildStorageKeySuffix(apiKey: string): string { | |||
* @public | |||
*/ | |||
export function offlineInit(config: IClientConfigSync): EppoClient { | |||
const isObfuscated = config.isObfuscated ?? false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
moved to class
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π₯
return client; | ||
} | ||
|
||
async function explicitInit(config: IClientConfig): Promise<EppoClient> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
moved to EppoJSClient.init
src/index.ts
Outdated
* @internal | ||
* @param config | ||
*/ | ||
async init(config: IClientConfig): Promise<EppoJSClient> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no changes here from explicitInit
* @internal | ||
* @param config | ||
*/ | ||
offlineInit(config: IClientConfigSync) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no changes from offline init
: initFromConfigStoreError | ||
? initFromConfigStoreError | ||
: new Error('Eppo SDK: No configuration source produced a valid configuration'); | ||
applicationLogger.warn( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The diff makes it hard to see what's left in this method;
- We keep the
forceReinitialize
check here to keep it out of the EppoJSClient.init method - Warn if an initialization is currently in progress
Just a thought here, since this will require a major version bump, does it even make sense to deprecate the old APIs? Since we're doing a major departure in terms of API design, it might not make sense to keep deprecated APIs around, as that will make the internal design a lot more complicated. Since upgrading will require code changes anyways (assuming it will be a major bump), then we might as well just redesign it as a blank slate, not worrying about deprecations. WDYT? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for carving out this PR, makes things very clear. I like what I see! π
Consider throwing a package.json
minor version bump in here just so you don't forget π
/** | ||
* @deprecated. use `getInstance()` instead. | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Guessing the plan in the future is to make this private? Or have getInstance() construct just-in time if one not set?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Affirmative to making this private, when we make the next major bump. The first step is to encourage everyone to move to the single accessor.
public static initialized = false; | ||
|
||
private initialized = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice
@@ -287,308 +584,42 @@ export function buildStorageKeySuffix(apiKey: string): string { | |||
* @public | |||
*/ | |||
export function offlineInit(config: IClientConfigSync): EppoClient { | |||
const isObfuscated = config.isObfuscated ?? false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π₯
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for iterating on all the comments!
The one thing I'm still confused about (probably because it's late π) is the deprecation of getInstance()
. Are we expecting people to handle managing the instance they get from buildAndInit()
? Does this mean that they would need to write their own getInstance()
methods and wrapper assignment methods with null checks that return default value if not initialized? If so, I think this may be unnecessary friction and we should think about how to make the 99% use case--a singleton in a code base where they are not concerned about conflicts--as friction-free as possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a thought here, since this will require a major version bump, does it even make sense to deprecate the old APIs?
The intent for this PR was no more than a minor bump. Is there a breaking change to the API I overlooked? @felipecsl
Since we're doing a major departure in terms of API design, it might not make sense to keep deprecated APIs around, as that will make the internal design a lot more complicated.
This I agree on, however, that's better left for #166. This PR doesn't change any if the current API, nor deprecates it (without an immediately available equivalent, ex: getInstance
instead of EppoJSClient.instance
)
Since upgrading will require code changes anyways (assuming it will be a major bump), then we might as well just redesign it as a blank slate, not worrying about deprecations. WDYT?
Again, the intent of this PR is no breaking changes.
The current focus is to deliver on the concrete ask of multiple instances and leveraging the opportunity to to incrementally work towards the ideal interface.
Deprecating with a replacement doesn't require a code change but if one is made to avoid the annotation, it's a very simple change. Accumulating some @deprecateds while we work out what the ideal is seems like a good tradeoff to allow us to continue to ship improvements and bug fixes to the current major version.
/** | ||
* @deprecated. use `getInstance()` instead. | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Affirmative to making this private, when we make the next major bump. The first step is to encourage everyone to move to the single accessor.
@aarsilv no deprecation of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is directionally correct, LGTM!
labels: mergeable
Eppo Internal
ποΈ towards FF-3888
π Multiple Eppo Clients
Configuration Side-loading
Motivation and Context
This PR is carved from #166 in an attempt to make #166 focused on the the "new" API. This PR adds no net new functionality, however, the new
EppoJSClient.init
method, marked @internal does allow for unbuffered initialization calls just as the globalinit
method previously allowed. This internal method can be made private once we remove the globalinit
method in the next major release.The
init
andgetInstance()
methods work on a singleton instance of theEppoJSClient
which makes for a generally smooth developer experience given that the Eppo SDK essentially handles dependency management for the dev. This does have its own drawbacks, but those are not particularly relevant here.There are use cases, however, when a non-singleton instance of the
EppoJSClient
is required. One such use case is embedding the Eppo SDK into a library which itself can be included in other applications. If that 3p library shipped with Eppo is integrated into another application that has the Eppo SDK running, there would be undefined behaviour as the SDK cannot handle this use case.TL;DR: We want to move away from singleton access and allow for multiple instance of the EppoJSClient while maintaining the current API
Description
instance
throughgetInstance()
EppoJSClient.instance
initialized
andensureInitialized
class members; still setEppoJSClient.initialized
as before to ensure backward compatibility with the current APIHow has this been documented?
How has this been tested?