Best Practices For Writing Clean Interfaces in Go

Interfaces in Go allow us to treat different types as the same data type temporarily because both types implement the same kind of behavior. They are central to a Go programmer’s toolbelt and are often used improperly by new Go developers… leading to hard to read and buggy code.

Before we dive in too deep, if you’re here looking to improve your Go skills and perhaps get certified along the way, take a look at the Go Mastery course I recently released. With that out of the way, let’s take a look at some of the best practices for Golang interfaces.

Recap on Interfaces

I often look to the standard library as an example of the way to write clean Go. The standard error interface is simple:

type error interface {
    Error() string

The error interface is implemented by any type that has an Error() method defined on it, and this simple trait makes writing logging and metrics implementations much simpler. That method accepts no parameters and returns a string. For example, let’s define a struct that represents a network problem:

type networkProblem struct {
	message string
	code    int

Then we define an Error() method:

func (np networkProblem) Error() string {
	return fmt.Sprintf("network error! message: %s, code: %v", np.message, np.code)

Now, we can use an instance of the networkProblem struct wherever an error is accepted.

func handleErr(err error) {

np := networkProblem{
	message: "we received a problem",
	code:    404,


// prints "network error! message: we received a problem, code: 404"

Keep Interfaces Small

If there is only one piece of advice that you take away from this article, make it this: keep interfaces small! Interfaces are meant to define the minimal behavior necessary to accurately represent an idea or concept.

Here is an example from the standard HTTP package of a larger interface that’s a good example of defining minimal behavior:

type File interface {
    Readdir(count int) ([]os.FileInfo, error)
    Stat() (os.FileInfo, error)

Any type that satisfies the interface’s behaviors can be considered by the HTTP package as a File. This is convenient because the HTTP package doesn’t need to know if it’s dealing with a file on disk, a network buffer, or a simple []byte.

Interfaces Should Have No Knowledge of Satisfying Types

An interface should define what is necessary for other types to classify as a member of that interface. They shouldn’t be aware of any types that happen to satisfy the interface at design time.

For example, let’s assume we are building an interface to describe the components necessary to define a car.

type car interface {
	Color() string
	Speed() int
	IsFiretruck() bool

Color() and Speed() make perfect sense, they are methods confined to the scope of a car. IsFiretruck() is an anti-pattern. We are forcing all cars to declare whether or not they are firetrucks. In order for this pattern to make any amount of sense, we would need a whole list of possible subtypes. IsPickup(), IsSedan(), IsTank()… where does it end??

Instead, the developer should have relied on the native functionality of type assertion to derive the underlying type when given an instance of the car interface. Or, if a sub-interface is needed, it can be defined as:

type firetruck interface {
	HoseLength() int

Which inherits the required methods from car and adds one additional required method to make the car a firetruck.

Interfaces Are Not Classes

  • Interfaces are not classes, they are slimmer.
  • Interfaces don’t have constructors or deconstructors that require that data is created or destroyed.
  • Interfaces aren’t hierarchical by nature, though there is syntactic sugar to create interfaces that happen to be supersets of other interfaces.
  • Interfaces define function signatures, but not underlying behavior. Making an interface often won’t DRY up your code in regards to struct methods. For example, if five types satisfy the error interface, they all need their own version of the Error() function.

Thanks for reading, now take a course!

Interested in a high-paying job in tech? Land interviews and pass them with flying colors after taking my hands-on coding courses.


Follow and hit me up on Twitter @q_vault if you have any questions or comments. If I’ve made a mistake in the article be sure to let me know so I can get it corrected!

Subscribe to my newsletter for more coding articles delivered straight to your inbox.

Related Work