Skip to content

Commit

Permalink
stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
omkarmoghe committed May 15, 2024
1 parent 7346f9b commit 04721d4
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 17 deletions.
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
lab_coat (0.1.4)
lab_coat (0.1.5)

GEM
remote: https://rubygems.org/
Expand Down
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ See the [`Experiment`](lib/lab_coat/experiment.rb) class for more details.
|`ignore?`|Whether or not the result should be ignored. Ignored `Results` are still passed to `#publish!`. Defaults to `false`, i.e. nothing is ignored.|
|`publishable_value`|The data to publish for a given `Observation`. This value is only for publishing and **is not** returned by `run!`. Defaults to `Observation#value`.|
|`raised`|Callback method that's called when an `Observation` raises.|
|`select_observation`|Override this method to select which observation's `value` should be returned by the `Experiment`. Defaults to the control `Observation`.|

> [!TIP]
> You should create a shared base class(es) to maintain consistency across experiments within your app.
Expand All @@ -91,7 +92,7 @@ class ApplicationExperiment < LabCoat::Experiment
def publish!(result)
payload = result.to_h.merge(
user_id: @user.id, # e.g. something from the `Experiment` state
build_number: context.version # e.g. something from the runtime context
build_number: context[:version] # e.g. something from the runtime context
)
YourO11yService.track_experiment_result(payload)
end
Expand Down Expand Up @@ -123,6 +124,21 @@ class ApplicationExperiment < LabCoat::Experiment
end
```

You might want to rollout the new code path in certain cases.

```ruby
# application_experiment.rb
class ApplicationExperiment < LabCoat::Experiment
def select_observation(result)
if result.matched? || YourFeatureFlagService.flag_enabled?(@user.id, @context[:rollout_flag_name])
candidate
else
super
end
end
end
```

### Make some `Observations` via `run!`

You don't have to create an `Observation` yourself; that happens automatically when you call `Experiment#run!`. The control and candidate `Observations` are packaged into a `Result` and [passed to `Experiment#publish!`](#publish-the-result).
Expand Down
29 changes: 14 additions & 15 deletions lib/lab_coat/experiment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,26 +57,25 @@ def publishable_value(observation)
observation.value
end

# Override this method to select which observation's `value` should be returned by the `Experiment`. Defaults to
# the control `Observation`. This method is only called if the `Experiment` is enabled. This is useful for rolling
# out new behavior in a controlled way.
# @param control [LabCoat::Observation] The control `Observation`.
# @param candidate [LabCoat::Observation] The candidate `Observation`.
# @return [TrueClass, FalseClass]
def select_observation(control, _candidate)
control
end

# Override this method to publish the `Result`. It's recommended to override this once in an application wide base
# class.
# @param result [LabCoat::Result] The result of this experiment.
# @return [void]
def publish!(result); end

# Override this method to select which observation's `value` should be returned by the `Experiment`. Defaults to
# the control `Observation`. This method is only called if the `Experiment` is enabled. This is useful for rolling
# out new behavior in a controlled way.
# @param result [LabCoat::Result] The result of the experiment.
# @return [TrueClass, FalseClass]
def select_observation(result)
result.control
end

# Runs the control and candidate and publishes the result. Always returns the result of `control`.
# @param context [Hash] Any data needed at runtime.
# @return [Object] An `Observation` value.
def run!(**context)
def run!(**context) # rubocop:disable Metrics/MethodLength
# Set the context for this run.
@context = context

Expand All @@ -93,11 +92,11 @@ def run!(**context)
result = Result.new(self, control_obs, candidate_obs)
publish!(result)

# Reset the context for this run.
@context = {}

# Always return the control.
select_observation(control_obs, candidate_obs).value
select_observation(result).value.tap do
# Reset the context for this run. Done here so that `select_observation` has access to the runtime context.
@context = {}
end
end
end
end

0 comments on commit 04721d4

Please sign in to comment.