--- name: go-functional-options description: Use when designing a Go constructor or factory function with optional configuration — especially with 3+ optional parameters or extensible APIs. Also use when building a New* function that takes many settings, even if they don't mention "functional options" by name. Does not cover general function design (see go-functions). --- # Functional Options Pattern Functional options is a pattern where you declare an opaque `Option` type that records information in an internal struct. The constructor accepts a variadic number of these options and applies them to configure the result. ## Resource Routing - `references/OPTIONS-VS-STRUCTS.md` - Read when choosing between config structs and functional options, implementing the full interface-based option pattern, or evaluating hybrid constructor APIs. ## When to Use Use functional options when: - **3+ optional arguments** on constructors or public APIs - **Extensible APIs** that may gain new options over time - **Clean caller experience** is important (no need to pass defaults) ## The Pattern ### Core Components 1. **Unexported `options` struct** - holds all configuration 2. **Exported `Option` interface** - with unexported `apply` method 3. **Option types** - implement the interface 4. **`With*` constructors** - create options ### Option Interface ```go type Option interface { apply(*options) } ``` The unexported `apply` method ensures only options from this package can be used. ## Comparison: Functional Options vs Config Struct | Aspect | Functional Options | Config Struct | |--------|-------------------|---------------| | **Extensibility** | Add new `With*` functions | Add new fields (may break) | | **Defaults** | Built into constructor | Zero values or separate defaults | | **Caller experience** | Only specify what differs | Must construct entire struct | | **Testability** | Options are comparable | Struct comparison | | **Complexity** | More boilerplate | Simpler setup | **Prefer Config Struct when**: Fewer than 3 options, options rarely change, all options usually specified together, or internal APIs only. ## Why Not Closures? The interface approach is preferred over closure-only options because: 1. **Testability** - Options can be compared in tests and mocks 2. **Debuggability** - Options can implement `fmt.Stringer` 3. **Flexibility** - Options can implement additional interfaces 4. **Visibility** - Option types are visible in documentation ## Quick Reference ```go // 1. Unexported options struct with defaults type options struct { field1 Type1 field2 Type2 } // 2. Exported Option interface, unexported method type Option interface { apply(*options) } // 3. Option type + apply + With* constructor type field1Option Type1 func (o field1Option) apply(opts *options) { opts.field1 = Type1(o) } func WithField1(v Type1) Option { return field1Option(v) } // 4. Constructor applies options over defaults func New(required string, opts ...Option) (*Thing, error) { o := options{field1: defaultField1, field2: defaultField2} for _, opt := range opts { opt.apply(&o) } // ... } ``` ### Checklist - [ ] `options` struct is unexported - [ ] `Option` interface has unexported `apply` method - [ ] Each option has a `With*` constructor - [ ] Defaults are set before applying options - [ ] Required parameters are separate from `...Option` ## Related Skills - **Interface design**: See [go-interfaces](../go-interfaces/SKILL.md) when designing the `Option` interface or choosing between interface and closure approaches - **Naming conventions**: See [go-naming](../go-naming/SKILL.md) when naming `With*` constructors, option types, or the unexported options struct - **Function design**: See [go-functions](../go-functions/SKILL.md) when organizing constructors within a file or formatting variadic signatures - **Documentation**: See [go-documentation](../go-documentation/SKILL.md) when documenting `Option` types, `With*` functions, or constructor behavior ### External Resources - [Self-referential functions and the design of options](https://commandcenter.blogspot.com/2014/01/self-referential-functions-and-design.html) - Rob Pike - [Functional options for friendly APIs](https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis) - Dave Cheney