Distributed Systems Made Easy!

Distributed Systems Made Easy!

Share this post

Distributed Systems Made Easy!
Distributed Systems Made Easy!
Golang Functional Options: Handling Configurations In a Flexible Manner

Golang Functional Options: Handling Configurations In a Flexible Manner

Unveiling the Elegance and Flexibility of the Functional Options Pattern, allowing you to develop clean and extendible code.

Pratik Pandey's avatar
Pratik Pandey
Dec 04, 2023
∙ Paid
2

Share this post

Distributed Systems Made Easy!
Distributed Systems Made Easy!
Golang Functional Options: Handling Configurations In a Flexible Manner
1
Share

The Functional Options Pattern in Go is an elegant and flexible way to handle configuration in your Go programs. This pattern offers a significant improvement over traditional configuration methods, such as using a struct or variadic parameters.

Challenges:

Let’s assume you’re building a library in Golang, and you want your clients to be able to configure your library according to their needs.

Let’s say you’re trying to build `Server` struct. Here’s what it looks like -

type Server struct {
    host string
    port int
    protocol string
    timeout time.Duration
    maxConnections int
}

Now, if you wanted to provide your Clients a way to create an instance of your struct, you can do it in the following ways -


Constructor with Multiple Parameters:

func NewServer(host string, port int, protocol string, timeout time.Duration, maxConnections int) *Server {
    return &Server{host, port, protocol, timeout, maxConnections}
}

Challenges

  • Scalability: As the number of configuration parameters increases, the constructor becomes unwieldy. Adding a new parameter requires changing the constructor signature, affecting all places where it's called.

  • Default Values: Handling default values for each parameter is complex. If some parameters are optional, managing defaults within the constructor becomes cumbersome.

  • Readability: Long lists of parameters make the constructor call difficult to read and maintain. It's easy to mix up the order of parameters, especially when they are of the same type.


Configuration Struct:

type ServerConfig struct {
    Host string
    Port int
    Protocol string
    Timeout time.Duration
    MaxConnections int
}

func NewServer(config ServerConfig) *Server {
    // Set defaults for zero values if needed
    if config.Timeout == 0 {
        config.Timeout = 30 * time.Second
    }
    // ... similar checks for other fields

    return &Server{
        host: config.Host,
        port: config.Port,
        protocol: config.Protocol,
        timeout: config.Timeout,
        maxConnections: config.MaxConnections,
    }
}

Challenges

  • Verbosity: For simple configurations, using a full struct can be overkill. It requires defining a new type and then creating an instance of this type, even for minimal configurations.

  • Zero Value Ambiguity: In Go, all types have a zero value. If a configuration parameter is not set, it's not clear whether the zero value is intentional or a mistake.

  • Mutable Configuration: As the config struct is usually passed as a pointer, it could be mutable, leading to potential side effects if the configuration is altered after the object is created.


This post is for paid subscribers

Already a paid subscriber? Sign in
© 2025 Pratik Pandey
Privacy ∙ Terms ∙ Collection notice
Start writingGet the app
Substack is the home for great culture

Share