Introduction Interfaces should only be used when their added value is clear. I see too many packages that declare interfaces unnecessarily, sometimes just for the sake of using interfaces. The use of interfaces when they are not necessary is called interface pollution. This is a practice I would like to see questioned and identified more in code reviews.
Code Example Let’s look at a code example that contains questionable design choices that raise flags for interface pollution.
Introduction I see a lot of developers coming to Go from object oriented programing languages such as C# and Java. Because these developers have been trained to use type hierarchies, it makes sense for them to use this same pattern in Go. However, there are aspects of Go that don’t allow type hierarchies to provide the same level of functionality they do in other object oriented programming languages. Specifically, the concepts of base types and subtyping don’t exist in Go so type reuse requires a different way of thinking.
Introduction If you are new to Linux or the Mac you might find installing Go to be a bit confusing. It was for me when I started learning Go. Go was the reason I stopped using Windows, which I used for 20 years. Even if you’re experienced with these operation systems, setting up Go might seem a bit of a mystery. With this in mind, let’s walk through installing Go.
I am constantly thinking about the Go language and how things work. Lately I have been thinking how everything in Go is by value. We see this when we pass values to functions, when we iterate over slices and when we perform type assertions. In every case, copies of the values that are stored in these data structures are returned. When I first started learning Go this threw me off, but I have come to appreciate the reasonability this brings to our code.
Composition goes beyond the mechanics of type embedding. It’s a paradigm we can leverage to design better APIs and to build larger programs from smaller parts. It all starts from the declaration and implementation of types that have a single purpose. Programs that are architected with composition in mind have a better chance to grow and adapt to changing needs. They are also much easier to read and reason about.
Go is an object oriented programming language. It may not have inheritance, but in this 20 minute video from the Bangalore meetup, I will show you how object oriented programming practices and techniques can be applied to your Go programs. From an object oriented standpoint, Go does provides the ability to add behavior to your types via methods, allows you to implement polymorphic behavior via interfaces and gives you a way to extend the state and behavior of any existing type via type embedding.
Introduction One of the things I love about Go is the profiling and debug information you can generate. There is a special environmental variable named GODEBUG that will emit debugging information about the runtime as your program executes. You can request summary and detailed information for both the garbage collector and the scheduler. What’s great is you don’t need to build your program with any special switches for it to work.
Having some basic skills in debugging Go programs can save any programmer a good amount of time trying to identify problems. I believe in logging as much information as you can, but sometimes a panic occurs and what you logged is not enough. Understanding the information in a stack trace can sometimes mean the difference between finding the bug now or needing to add more logging and waiting for it to happen again.
I am asked quite a bit about when and when not to use pointers in Go. The problem most people have, is that they try to make this decision based on what they think the performance tradeoff will be. Hence the problem, don’t make coding decisions based on unfounded thoughts you may have about performance. Make coding decisions based on the code being idiomatic, simple, readable and reasonable.
My use of pointers is based on discoveries I have made looking at code from the standard library.
In part I of this post, we learned about the error interface and how the standard library provides support for creating error interface values via the errors package. We also learned how to work with error interface values and use them to identify when an error has occured. Finally, we saw how some packages in the standard library export error interface variables to help us identify specific errors.
Knowing when to create and use custom error types in Go can sometimes be confusing.