One way to become very good at something is to practice repeatedly. Like Depeche Mode said in one of their songs: “I like to practice what I (p)reach”. Each time I encounter the same or a similar problem, I find a better, more elegant solution. If you have read my , you know I am fond of Go. This time, I'd like to start a series of articles on design patterns in Go and will begin with the one I use most frequently - the Builder pattern. previous articles I frequently work with REST APIs and sometimes the response that I have to construct and serve to the end user is a complex data structure with many optional properties. In such cases, I avoid constructing the response within a single method call and instead use the builder pattern. This keeps my code clean and organized, and also allows me to easily test isolated bits of logic. Let's consider the following relatively simple example: a response that will include a user, account, and address details. type Response struct { User User Account Account Address Address } type User struct { Email string } type Account struct { Balance float64 } type Address struct { City string } Imagine I have obtained all the data from a database or some other sources and now I need to map it to a Response struct using the Builder pattern. To do this, I will use a new struct named ResponseBuilder type ResponseBuilder struct { Response Response } func NewBuilder() *ResponseBuilder { return &ResponseBuilder{ Response: Response{}, } } The function is a convenient method that returns a pointer to a with all the expected zero-value data structures initialized. With this in place, I can start adding helper methods to the builder for constructing my response. NewBuilder ResponseBuilder func (rb *ResponseBuilder) SetEmail(email string) { // can perform email validation here if needed rb.Response.User.Email = email } func (rb *ResponseBuilder) SetBalance(bal float64) { rb.Response.Account.Balance = bal } func (rb *ResponseBuilder) SetCity(city string) { rb.Response.Address.City = city } func (rb *ResponseBuilder) Build() Response { return rb.Response } func main() { rb := NewBuilder() rb.SetEmail("foo@bar.com") rb.SetBalance(100.54) rb.SetCity("London") fmt.Printf("response %+v\n", rb.Build()) } And that is it for a basic example, although it might not look like much. Now imagine that each method has some additional logic, such as validation on the value being passed in. In that case, each function will still only do one thing and do it well, which is good practice. Can it get any better than this? It depends. I could apply the Fluent Interface Design Pattern to allow method chaining and further reduce the amount of code in the function. The implementation would look like this: main package main type ResponseBuilder struct { Response Response } func NewBuilder() *ResponseBuilder { return &ResponseBuilder{ Response: Response{}, } } type Response struct { User User Account Account Address Address } type User struct { Email string } type Account struct { Balance float64 } type Address struct { City string } func (rb *ResponseBuilder) SetEmail(email string) *ResponseBuilder { // can perform email validation here if needed rb.Response.User.Email = email return rb } func (rb *ResponseBuilder) SetBalance(bal float64) *ResponseBuilder { rb.Response.Account.Balance = bal return rb } func (rb *ResponseBuilder) SetCity(city string) *ResponseBuilder { rb.Response.Address.City = city return rb } func (rb *ResponseBuilder) Build() Response { return rb.Response } func main() { rb := NewBuilder(). SetEmail("foo@bar.com"). SetBalance(100.54). SetCity("London") fmt.Printf("response %+v\n", rb.Build()) } Arguably, the above example is a bit better. Each method now assigns a value, as it did previously, and returns a pointer to itself, which allows me to chain method calls. ResponseBuilder In some scenarios, you might end up with more complex data structures and many more fields to be assigned to the response. In cases like this, it can negatively impact readability. To make it easier to follow, we can break the main builder into multiple smaller ones, each responsible for its own subset of data. Consider the following example: package main type Response struct { User User Account Account Address Address } type User struct { Email string } type Account struct { Balance float64 } type Address struct { City string } type ResponseBuilder struct { Response Response } func NewBuilder() *ResponseBuilder { return &ResponseBuilder{ Response: Response{}, } } func (rb *ResponseBuilder) User() *UserBuilder { return &UserBuilder{*rb} } func (rb *ResponseBuilder) Account() *AccountBuilder { return &AccountBuilder{*rb} } func (rb *ResponseBuilder) Address() *AddressBuilder { return &AddressBuilder{*rb} } func (rb *ResponseBuilder) Build() Response { return rb.Response } type UserBuilder struct { ResponseBuilder } func (ub *UserBuilder) Email(email string) *UserBuilder { ub.Response.User.Email = email return ub } type AccountBuilder struct { ResponseBuilder } func (ab *AccountBuilder) Balance(bal float64) *AccountBuilder { ab.Response.Account.Balance = bal return ab } type AddressBuilder struct { ResponseBuilder } func (addrb *AddressBuilder) City(city string) *AddressBuilder { addrb.Response.Address.City = city return addrb } func main() { rb := NewBuilder(). User(). Email("foo@bar.com") Account(). Balance(100.54). Address(). City("London") fmt.Printf("response %+v\n", rb.Build()) } In the above example, I've broken down a single builder into multiple builders, each dealing with its own subset of data, but all utilizing the same and having access to struct. ResponseBuilder Response You won't see much value in such a basic example, but when requirements grow and there is a lot of data to work with, this approach might become useful as it becomes more clear what data is being assigned where. And that's all folks! The Builder pattern is a great way to keep your code organized and maintainable, and with a few modifications, it can make your life easier and your code cleaner.