This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
samber/ro is a Go implementation of the ReactiveX spec β a library for reactive/stream programming with Observables, Observers, and Operators. It uses Go 1.18+ generics extensively. The library is v0 and follows SemVer strictly.
make build # Build all modules
make test # Run all tests with race detector
make lint # Run golangci-lint + license header check
make lint-fix # Auto-fix lint issues
make bench # Run benchmarks
make coverage # Generate coverage report (cover.html)Run a single test:
go test -race -run TestFunctionName ./...Run tests for a specific plugin:
cd plugins/encoding/json && go test -race ./...This is a Go workspace (go.work) with many independent modules. The root module is github.com/samber/ro. Each plugin under plugins/ has its own go.mod. Some plugins are commented out in go.work because they require newer Go versions.
The SIMD plugin (plugins/exp/simd) requires GOEXPERIMENT=simd and GOWORK=off to build/test.
- Root package (
ro) β Core types and all built-in operators internal/β Internal helpers:xsync(mutex wrappers),xatomic,xrand,xtime,xerrors,constraintstesting/β PackagerotestingwithAssertSpec[T]interface for fluent test assertionsplugins/β Each plugin is a separate Go module with its owngo.mod. Categories: encoding, observability, rate limiting, I/O, data manipulation, etc.ee/β Enterprise Edition (separate license). Containsotelandprometheusplugins, plus licensing infrastructureexamples/β Working example applications (each is its own module)docs/β Docusaurus documentation site. Has its ownCLAUDE.mdfor doc-writing conventions
All chainable operators follow this signature pattern:
func OperatorName[T any](params) func(Observable[T]) Observable[R]Example:
func Filter[T any](predicate func(item T) bool) func(Observable[T]) Observable[T] {
return func(source Observable[T]) Observable[T] {
return NewUnsafeObservableWithContext(func(subscriberCtx context.Context, destination Observer[T]) Teardown {
sub := source.SubscribeWithContext(
subscriberCtx,
NewObserverWithContext(
func(ctx context.Context, value T) {
ok := predicate(value)
if ok {
destination.NextWithContext(ctx, value)
}
},
destination.ErrorWithContext,
destination.CompleteWithContext,
),
)
return sub.Unsubscribe
})
}
}They return a function that transforms one Observable into another, enabling composition via Pipe().
Most operators have variants created by combining these suffixes:
Iβ Adds anindex int64parameter to the callback (e.g.,FilterI,MapI)WithContextβ Addscontext.Contextto the callback signature (e.g.,FilterWithContext,MapWithContext)Errβ The callback can return anerrorthat terminates the stream (e.g.,MapErr)
These suffixes combine in a fixed order: Err + I + WithContext. Examples:
MapβMapIβMapWithContextβMapIWithContextMapErrβMapErrIβMapErrWithContextβMapErrIWithContext
Other naming patterns:
- Numbered suffixes (2, 3, 4, 5...) β Arity variants for multi-observable operators (e.g.,
CombineLatest2,Zip3,MergeWith1). Also used for type-safe pipe:Pipe1throughPipe11 Opβ Operator version of a creation function, for use insidePipe()(e.g.,PipeOp)
Core operators live in the root ro package and have no external dependencies beyond samber/lo. They cover the standard ReactiveX operator categories (creation, transformation, filtering, combining, etc.) and are imported as github.com/samber/ro.
Plugins are separate Go modules under plugins/, each with its own go.mod and third-party dependencies. They follow the same func(Observable[T]) Observable[R] signature pattern and compose with core operators via Pipe(). Plugins wrap external libraries (e.g., zap, sentry, fsnotify) or provide domain-specific operators (e.g., JSON encoding, CSV I/O, rate limiting). Import them separately, e.g., github.com/samber/ro/plugins/encoding/json.
- Tests use
testifyassertions andgo.uber.org/goleakfor goroutine leak detection - Test files follow Go convention:
foo_test.goalongsidefoo.go - Example tests use
_example_test.gosuffix - The
plugins/testifyplugin provides reactive stream assertion helpers
Typical test pattern β use Collect() to gather all emitted values and assert:
func TestOperatorTransformationMap(t *testing.T) {
t.Parallel()
is := assert.New(t)
values, err := Collect(
Map(func(v int) int { return v * 2 })(Just(1, 2, 3)),
)
is.Equal([]int{2, 4, 6}, values)
is.NoError(err)
// Test error propagation
values, err = Collect(
Map(func(v int) int { return v * 2 })(Throw[int](assert.AnError)),
)
is.Equal([]int{}, values)
is.EqualError(err, assert.AnError.Error())
}Always test edge cases with Empty[T]() (empty source) and Throw[T](assert.AnError) (error source). Also test early unsubscription, context propagation, and context cancellation where applicable.
- Operator naming: Must be self-explanatory and respect ReactiveX/RxJS standards. Inspired by https://reactivex.io/documentation/operators.html and https://rxjs.dev/api
- Context propagation: Operators must not break the context chain. Always use
SubscribeWithContext,NextWithContext,ErrorWithContext,CompleteWithContext. TheWithContextvariant callbacks receive and return acontext.Context - Callback naming:
predicatefor bool-returning callbacks,transform/projectfor value-transforming callbacks,callbackfor void callbacks - Variadic operators: Some operators accept
...Observable[T]for flexibility (e.g.,Zip,Merge,MergeWith) - Type aliases: Some operators use
~[]Tconstraints to accept named slice types, not just[]T(e.g.,Flatten) - Documentation: Each operator needs a Go Playground link in its comment, a markdown doc in
docs/data/, an example inro_example_test.go, and an entry indocs/static/llms.txt - License headers: All
.gofiles require license headers (licenses/header.apache.txtfor open source,licenses/header.ee.txtforee/). Runmake lintto verify - Update the documentation: when updating a feature of the project, you MUST update the documentation. See @./docs/CLAUDE.md
- Contribution guidelines: @./docs/docs/contributing.md
- Extending ro guidelines: @./docs/docs/hacking.md
- Documentation guidelines: @./docs/CLAUDE.md
- Troubleshooting guidelines: @./docs/docs/troubleshooting/
- If you need more context on the project, read the LLMs documentation: @./docs/static/llms.txt