You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This may end up becoming a FAQ, so I wanted to write a few notes on why policies use builders. There were two main options with how we could design an API to configure policies:
Use structs to directly set configuration
Use methods
Since Failsafe-go configurations can be complex internally, and since we want to potentially validate settings in the future, it makes sense to configure policies using methods rather than setting fields directly on structs. That being the case, it also makes sense to just define policies as interfaces rather than structs, since no fields will be visible and using factory methods to construct them allows us to define good defaults.
That leaves us with the question of how we want our configuration methods to be designed. A few options include:
Make policies mutable, via configuration methods
Make policies immutable, where each configuration method returns a new copy
Make policies immutable, but built via mutable builders
Make policies immutable, but built via functional options
A drawback of option 1 is it would add more overhead when making policies concurrency safe, since we'd have to do an atomic operation or take a lock just to read simple variables like a RetryPolicy's MaxRetries.
A drawback of option 2 is that we'd add more overhead when constructing policies, which for high volume usage could be a problem. Another drawback with option 2 (and 1) is that placing policy configuration directly on the policies themselves would result in a larger API surface area in policies like CircuitBreaker, where configuring and operating the policy already offer several methods.
A drawback of option 3 is that while some Go libraries use builders (like the k8s REST client) it's not common. It also creates an additional object allocation to create the builder, though fewer than option 2. On the positive side, option 3 allows us to avoid concurrency safety problems when frequently reading policy settings internally.
A drawback of option 4 is that functional options don't have the same easy discoverability of builder methods, which benefit from code completion. They're also more verbose to use than builders, since each option has to be referenced by the package name. The only benefit that functional options offer, the ability for users to define their own options, isn't relevant here.
That all being the case, option 3 seemed to have the most positives and least negatives, leaning heavily towards an API that is user friendly.
The text was updated successfully, but these errors were encountered:
This may end up becoming a FAQ, so I wanted to write a few notes on why policies use builders. There were two main options with how we could design an API to configure policies:
Since Failsafe-go configurations can be complex internally, and since we want to potentially validate settings in the future, it makes sense to configure policies using methods rather than setting fields directly on structs. That being the case, it also makes sense to just define policies as interfaces rather than structs, since no fields will be visible and using factory methods to construct them allows us to define good defaults.
That leaves us with the question of how we want our configuration methods to be designed. A few options include:
That all being the case, option 3 seemed to have the most positives and least negatives, leaning heavily towards an API that is user friendly.
The text was updated successfully, but these errors were encountered: