Best Practices For Writing Clean Interfaces in Go
Tags: engineering, go, programming, tutorial
Published: 2020-03-15 14:34:34

---
title: Best Practices For Writing Clean Interfaces in Go
published: true
date: 2020-03-15 14:32:21 UTC
tags: engineering,golang,programming,tutorial
canonical_url: https://qvault.io/2020/03/15/best-practices-for-writing-clean-interfaces-in-go/?utm_source=rss&utm_medium=rss&utm_campaign=best-practices-for-writing-clean-interfaces-in-go
---

![](https://qvault.io/wp-content/uploads/2020/03/photo-1524439210809-5b21a2e18582-1024x639.jpeg)

Interfaces allow us to treat different types as the same type when it comes to specific behaviors. They are central to a Go programmers toolbelt and are often used improperly by new Go developers… leading to hard to read and buggy code.

* * *

_Interfaces are named collections of method signatures, they are how we achieve a kind of polymorphism in Go._  
[Click To Tweet](https://twitter.com/intent/tweet?url=https%3A%2F%2Fwp.me%2Fpb7Qei-fJ&text=Interfaces%C2%A0are%20named%20collections%20of%20method%20signatures%2C%20they%20are%20how%20we%20achieve%20a%20kind%20of%20polymorphism%20in%20Go.&via=wagslane&related=wagslane)

* * *

## Recap on Interfaces

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

```
type error interface {
    Error() string
}
```

By definition, the **error** interface encapsulates any **type** that has an _Error()_ method defined on it, 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) {
	fmt.Println(err.Error())
}

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

handleErr(np)

// 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 some entity.

Here is an example of a larger interface that remains a good example of defining minimal behavior:

```
type File interface {
    io.Closer
    io.Reader
    io.Seeker
    Readdir(count int) ([]os.FileInfo, error)
    Stat() (os.FileInfo, error)
}
```

_http.File_ – [https://golang.org/pkg/net/http/#pkg-overview](https://golang.org/pkg/net/http/#pkg-overview)

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 {
	GetColor() string
	GetSpeed() int
	IsFiretruck() bool
}
```

_GetColor()_ and _GetSpeed()_ 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](https://yourbasic.org/golang/type-assertion-switch/) 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 {
	car
	HoseLength() int
}
```

Which inherits the required methods from **car** and adds one additional required method.

## 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**. This means that making an interface often won’t DRY up your code in regards to struct methods. If five types satisfy the error interface, they all need their own version of the _Error()_ function.

## Thanks For Reading

Hit me up on twitter [@wagslane](https://twitter.com/wagslane) if you have any questions or comments.

Lane on Dev.to: [wagslane](https://dev.to/wagslane)

The post [Best Practices For Writing Clean Interfaces in Go](https://qvault.io/2020/03/15/best-practices-for-writing-clean-interfaces-in-go/) appeared first on [Qvault](https://qvault.io).