gostack
My curated Go library stack - opinionated take on Sam Berthe's github.com/samber/* ecosystem. Generics-first, zero-dep, prolifically maintained. This skill is the decision matrix on top of his work: when to reach for each library, when to deliberately not, and which versions are battle-tested today.
Not a fork of upstream documentation. The libraries are Sam Berthe's; the opinions are mine, with verified-current versions pinned at the top of each reference.
When to load which reference
The model: SKILL.md is the dispatcher. Open exactly one reference file based on the task at hand.
| If the task involves... |
Open |
Library |
| Collection transforms (Map, Filter, Reduce, GroupBy, Chunk, Flatten, Uniq, Zip…) |
references/lo.md |
samber/lo |
| Errors that need context, attributes, stack traces, public messages, or APM grouping |
references/oops.md |
samber/oops |
| Dependency injection container - services, scopes, lifecycle, graceful shutdown |
references/do.md |
samber/do (v2) |
| Optional/Result/Either monads, FP composition, nullable JSON fields |
references/mo.md |
samber/mo |
| Structured logging - handler composition, sampling, PII formatting, HTTP middleware, cloud sinks |
references/slog.md |
samber/slog-* ecosystem |
| In-memory cache with LRU/LFU/W-TinyLFU/S3FIFO, TTL, loaders, singleflight |
references/hot.md |
samber/hot (pre-1.0) |
| Reactive streams, infinite event pipelines, multi-source async combine |
references/ro.md |
samber/ro (pre-1.0) |
My one-line take on each
| Library |
Reach for it when… |
Skip it when… |
| lo |
Stdlib slices/maps doesn't cover the transform (Map/Filter/Reduce/GroupBy/Chunk/Flatten) |
The stdlib does cover it. Don't pull lo.Contains if slices.Contains exists. |
| oops |
Errors cross architectural boundaries (handler ↔ service ↔ repo) and need to land cleanly in APM |
Inside leaf functions where plain fmt.Errorf("…: %w", err) is enough |
| do v2 |
Service graph >~20 nodes, lifecycle management matters, want runtime container |
Small services where manual constructor wiring fits on one screen. Never use v1. |
| mo |
JSON nullable fields (Option[T]), FP-leaning team |
Production code paths where if err != nil reads better to Go reviewers |
| slog-multi / sampling / formatter |
Logging pipeline needs routing, sampling, or PII scrubbing before sinks |
Single-handler stdout/JSON setup. Don't pipe what you don't route. |
| hot |
Read-through cache with loader + singleflight on warm read paths |
Anything OK with patrickmn/go-cache or where stdlib sync.Map + TTL goroutine suffices. Pre-1.0 - pin the version. |
| ro |
Multi-source async pipeline that would be a 200-line select mess |
Almost everything else in Go. Pre-1.0, niche, default away from it. Try errgroup + channels first. |
Hard rules I enforce
- Verify version before adopting. GitHub release pages move - confirm the tag in
metadata.upstreams is still current before pinning in go.mod. The versions-verified date is when I last looked.
- Pre-1.0 libraries (
hot, ro) require version pinning. No ^0.x ranges. Pin exact in go.mod and gate updates through a code review.
- Don't use
samber/do v1. It's unmaintained. v2 is incompatible - full migration only.
lo.Must, mo.MustGet, do.MustInvoke are panic-on-error. Allowed in tests, init, and inside mo.Do blocks. Not allowed in production request paths.
oops error messages must be low-cardinality. Variable data goes in .With("key", value), not interpolated into the message string. APM grouping breaks otherwise.
- Sampling-first ordering for slog. Place sampling outermost - formatting records that get sampled out wastes CPU.
samber/lo is not a replacement for stdlib. Prefer slices.Contains, slices.Sort, maps.Keys, maps.Values when the stdlib covers it. lo earns its keep on the things the stdlib doesn't.
How adoption cascades (my mental model)
If a Go service is going to adopt this ecosystem, the order I'd introduce libraries:
oops first - biggest leverage. Every service has errors; structured errors transform on-call triage.
slog-multi + slog-sampling - once errors are structured, route them properly.
lo - once code is being written/reviewed, the Map/Filter/Reduce idioms compound.
do v2 - when the constructor wiring in main.go exceeds one screen.
mo - only if the team has FP literacy; otherwise it's friction.
hot - only when a specific read-through cache pattern shows up; not as a general "always cache" tool.
ro - last resort. If a problem demands it, you'll know.
Where this skill plugs into the rest
vd:py2go (Python→Go migration) - defaults to slog over zap/zerolog, mentions samber/oops for error context, samber/do for DI when the graph grows. See py2go's translation rules table for the cross-reference.
vd:cook / vd:ship - the lint and review steps benefit from understanding why a PR pulls in samber/lo over stdlib (lo.Contains vs slices.Contains is a code-review smell I want to catch).
vd:debug / vd:fix - samber/oops error chains preserve the context that makes debugging much faster; this skill is the place to confirm best practices.
Versions snapshot (verified 2026-05-23)
| Library |
Pinned |
Released |
Maintenance |
| samber/lo |
v1.53.0 |
2026-03-02 |
Active (21.2k stars) |
| samber/oops |
v1.21.0 |
2026-01-18 |
Active |
| samber/do |
v2.0.0 |
2025-09-21 |
v2 only - v1 dead |
| samber/mo |
v1.16.0 |
2025-09-25 |
Active |
| samber/slog-multi |
v1.8.0 |
2026-03-25 |
Active |
| samber/slog-sampling |
v1.6.0 |
2026-03-25 |
Active |
| samber/slog-formatter |
v1.3.0 |
2026-03-25 |
Active |
| samber/slog-echo |
v2.0.0 |
2026-05-04 |
v2 breaking change |
| samber/slog-loki |
v3.7.2 |
2026-03-25 |
On v3 |
| samber/slog-gin / chi / fiber |
v1.x |
Apr-May 2026 |
Active |
| samber/slog-datadog / sentry |
v2.x |
Mar 2026 |
Active |
| samber/hot |
v0.13.0 |
2026-03-11 |
PRE-1.0 - pin exact |
| samber/ro |
v0.3.0 |
2026-03-02 |
PRE-1.0, young |
If a version drifts ahead by a major release, re-verify before recommending - especially for the pre-1.0 libraries.
Adding a new library
Future extension flow:
- Verify the new
samber/* repo (active, license, last release) via gh api repos/samber/<name>.
- Add a row to
metadata.upstreams in this SKILL.md with the pinned version and date.
- Drop a new
references/<lib>.md following the structure of lo.md (identity → my take → version + install → core patterns → mistakes → when to skip).
- Add a row to "When to load which reference" and "My one-line take on each" tables above.
1---2name: gostack3description: My curated Go stack reference - opinionated guide to Sam Berthe's libraries (lo Lodash helpers, oops structured errors, do v2 DI, mo monads, slog logging ecosystem, hot in-memory cache pre-1.0, ro reactive streams pre-1.0). Use when writing or reviewing Go code that imports github.com/samber/*, when deciding between these libraries and the stdlib, when adopting or upgrading any of them, or when scaffolding a new Go service and considering this ecosystem.4license: MIT5---67# gostack89My curated Go library stack - opinionated take on Sam Berthe's `github.com/samber/*` ecosystem. Generics-first, zero-dep, prolifically maintained. This skill is the decision matrix on top of his work: when to reach for each library, when to deliberately not, and which versions are battle-tested **today**.1011Not a fork of upstream documentation. The libraries are Sam Berthe's; the opinions are mine, with verified-current versions pinned at the top of each reference.1213## When to load which reference1415The model: SKILL.md is the dispatcher. Open exactly one reference file based on the task at hand.1617| If the task involves... | Open | Library |18|---|---|---|19| Collection transforms (Map, Filter, Reduce, GroupBy, Chunk, Flatten, Uniq, Zip…) | [references/lo.md](references/lo.md) | `samber/lo` |20| Errors that need context, attributes, stack traces, public messages, or APM grouping | [references/oops.md](references/oops.md) | `samber/oops` |21| Dependency injection container - services, scopes, lifecycle, graceful shutdown | [references/do.md](references/do.md) | `samber/do` (v2) |22| Optional/Result/Either monads, FP composition, nullable JSON fields | [references/mo.md](references/mo.md) | `samber/mo` |23| Structured logging - handler composition, sampling, PII formatting, HTTP middleware, cloud sinks | [references/slog.md](references/slog.md) | `samber/slog-*` ecosystem |24| In-memory cache with LRU/LFU/W-TinyLFU/S3FIFO, TTL, loaders, singleflight | [references/hot.md](references/hot.md) | `samber/hot` (pre-1.0) |25| Reactive streams, infinite event pipelines, multi-source async combine | [references/ro.md](references/ro.md) | `samber/ro` (pre-1.0) |2627## My one-line take on each2829| Library | Reach for it when… | Skip it when… |30|---|---|---|31| **lo** | Stdlib `slices`/`maps` doesn't cover the transform (Map/Filter/Reduce/GroupBy/Chunk/Flatten) | The stdlib **does** cover it. Don't pull `lo.Contains` if `slices.Contains` exists. |32| **oops** | Errors cross architectural boundaries (handler ↔ service ↔ repo) and need to land cleanly in APM | Inside leaf functions where plain `fmt.Errorf("…: %w", err)` is enough |33| **do** v2 | Service graph >~20 nodes, lifecycle management matters, want runtime container | Small services where manual constructor wiring fits on one screen. **Never use v1.** |34| **mo** | JSON nullable fields (`Option[T]`), FP-leaning team | Production code paths where `if err != nil` reads better to Go reviewers |35| **slog-multi / sampling / formatter** | Logging pipeline needs routing, sampling, or PII scrubbing before sinks | Single-handler stdout/JSON setup. Don't pipe what you don't route. |36| **hot** | Read-through cache with loader + singleflight on warm read paths | Anything OK with `patrickmn/go-cache` or where stdlib `sync.Map` + TTL goroutine suffices. **Pre-1.0 - pin the version.** |37| **ro** | Multi-source async pipeline that would be a 200-line `select` mess | Almost everything else in Go. **Pre-1.0, niche, default away from it.** Try `errgroup` + channels first. |3839## Hard rules I enforce40411. **Verify version before adopting.** GitHub release pages move - confirm the tag in `metadata.upstreams` is still current before pinning in `go.mod`. The `versions-verified` date is when I last looked.422. **Pre-1.0 libraries (`hot`, `ro`) require version pinning.** No `^0.x` ranges. Pin exact in `go.mod` and gate updates through a code review.433. **Don't use `samber/do` v1.** It's unmaintained. v2 is incompatible - full migration only.444. **`lo.Must`, `mo.MustGet`, `do.MustInvoke` are panic-on-error.** Allowed in tests, init, and inside `mo.Do` blocks. **Not allowed in production request paths.**455. **`oops` error messages must be low-cardinality.** Variable data goes in `.With("key", value)`, not interpolated into the message string. APM grouping breaks otherwise.466. **Sampling-first ordering for slog.** Place sampling outermost - formatting records that get sampled out wastes CPU.477. **`samber/lo` is not a replacement for stdlib.** Prefer `slices.Contains`, `slices.Sort`, `maps.Keys`, `maps.Values` when the stdlib covers it. `lo` earns its keep on the things the stdlib doesn't.4849## How adoption cascades (my mental model)5051If a Go service is going to adopt this ecosystem, the order I'd introduce libraries:52531. **`oops` first** - biggest leverage. Every service has errors; structured errors transform on-call triage.542. **`slog-multi` + `slog-sampling`** - once errors are structured, route them properly.553. **`lo`** - once code is being written/reviewed, the Map/Filter/Reduce idioms compound.564. **`do` v2** - when the constructor wiring in `main.go` exceeds one screen.575. **`mo`** - only if the team has FP literacy; otherwise it's friction.586. **`hot`** - only when a specific read-through cache pattern shows up; not as a general "always cache" tool.597. **`ro`** - last resort. If a problem demands it, you'll know.6061## Where this skill plugs into the rest6263- `vd:py2go` (Python→Go migration) - defaults to `slog` over zap/zerolog, mentions `samber/oops` for error context, `samber/do` for DI when the graph grows. See py2go's translation rules table for the cross-reference.64- `vd:cook` / `vd:ship` - the lint and review steps benefit from understanding why a PR pulls in `samber/lo` over stdlib (`lo.Contains` vs `slices.Contains` is a code-review smell I want to catch).65- `vd:debug` / `vd:fix` - `samber/oops` error chains preserve the context that makes debugging much faster; this skill is the place to confirm best practices.6667## Versions snapshot (verified 2026-05-23)6869| Library | Pinned | Released | Maintenance |70|---|---|---|---|71| samber/lo | v1.53.0 | 2026-03-02 | Active (21.2k stars) |72| samber/oops | v1.21.0 | 2026-01-18 | Active |73| samber/do | v2.0.0 | 2025-09-21 | **v2 only - v1 dead** |74| samber/mo | v1.16.0 | 2025-09-25 | Active |75| samber/slog-multi | v1.8.0 | 2026-03-25 | Active |76| samber/slog-sampling | v1.6.0 | 2026-03-25 | Active |77| samber/slog-formatter | v1.3.0 | 2026-03-25 | Active |78| samber/slog-echo | **v2.0.0** | 2026-05-04 | **v2 breaking change** |79| samber/slog-loki | v3.7.2 | 2026-03-25 | On v3 |80| samber/slog-gin / chi / fiber | v1.x | Apr-May 2026 | Active |81| samber/slog-datadog / sentry | v2.x | Mar 2026 | Active |82| samber/hot | **v0.13.0** | 2026-03-11 | **PRE-1.0 - pin exact** |83| samber/ro | **v0.3.0** | 2026-03-02 | **PRE-1.0, young** |8485If a version drifts ahead by a major release, re-verify before recommending - especially for the pre-1.0 libraries.8687## Adding a new library8889Future extension flow:901. Verify the new `samber/*` repo (active, license, last release) via `gh api repos/samber/<name>`.912. Add a row to `metadata.upstreams` in this SKILL.md with the pinned version and date.923. Drop a new `references/<lib>.md` following the structure of `lo.md` (identity → my take → version + install → core patterns → mistakes → when to skip).934. Add a row to "When to load which reference" and "My one-line take on each" tables above.