Summary
Batch.Go(ctx, s, procs ...Processor[T]) currently accepts zero processors. With no processors the pipeline reads the source, discards every item, surfaces source errors, and fires Done(). That is a silent-data-discard footgun: forgetting to pass a processor produces a clean, error-free run that did nothing with the data — the same class of issue as a nil source (now ErrNilSource, see #64).
Proposal
Require at least one Processor. Callers who genuinely want to drain/discard a source pass an explicit no-op (&processor.Nil[T]{}, which already exists), so the intent is explicit at the call site instead of implicit.
Why
Tradeoffs to weigh
- Breaks variadic ergonomics for the degenerate zero-stage case.
- Breaks graceful handling of dynamically built processor lists that can legitimately be empty — callers would need to guard or substitute
processor.Nil.
- Breaks existing tests that assert no-processors is allowed:
TestBatch_NoProcessors (batch/batch_test.go) and the empty-processor-slice case in batch/error_handling_test.go.
- Breaking change (v0) → migration note + CHANGELOG entry.
Alternatives considered
- Document current behavior — keep allowing zero processors, add a godoc line that it drains and discards items (source errors still surface). Lower friction, keeps the footgun.
- Leave entirely as-is.
References
Summary
Batch.Go(ctx, s, procs ...Processor[T])currently accepts zero processors. With no processors the pipeline reads the source, discards every item, surfaces source errors, and firesDone(). That is a silent-data-discard footgun: forgetting to pass a processor produces a clean, error-free run that did nothing with the data — the same class of issue as a nil source (nowErrNilSource, see #64).Proposal
Require at least one
Processor. Callers who genuinely want to drain/discard a source pass an explicit no-op (&processor.Nil[T]{}, which already exists), so the intent is explicit at the call site instead of implicit.Gowith zero processors returns a start error (e.g.ErrNoProcessors), consistent with theErrNilSource/ErrBatchUsedcontract added in feat(batch)!: single-use Batch + Go returns (<-chan error, error); inline item IDs #64.b.Go(ctx, src, &processor.Nil[T]{}).Why
ErrNilSource, reused Batch →ErrBatchUsed).Tradeoffs to weigh
processor.Nil.TestBatch_NoProcessors(batch/batch_test.go) and the empty-processor-slice case inbatch/error_handling_test.go.Alternatives considered
References
ErrNilSource,ErrBatchUsed) and theExecuteBatchesnil-source fix that motivated this question.Batch.doProcessors(batch/batch.go); the no-op no-processors path is exercised byTestBatch_NoProcessors.