Subscribe to the Ardan Labs Insider

You’ll get our FREE Video Series & special offers on upcoming training events along with notifications on our latest blog posts.

Included in your subscription
  • Access to our free video previews
  • Updates on our latest blog posts
  • Discounts on upcoming events

Valid email required.

Submit failed. Try again or message us directly at

Thank You for Subscribing

Check your email for confirmation.

Avoid Interface Pollution

Author image
William Kennedy


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.

Listing 1:

01 package tcp
03 // Server defines a contract for tcp servers.
04 type Server interface {
05	Start() error
06	Stop() error
07	Wait() error
08 }
10 // server is our Server implementation.
11 type server struct {
12     /* impl */
13 }
15 // NewServer returns an interface value of type Server
16 // with an xServer implementation.
17 func NewServer(host string) Server {
18	return &server{host}
19 }
21 // Start allows the server to begin to accept requests.
22 func (s *server) Start() error {
23     /* impl */
24 }
26 // Stop shuts the server down.
27 func (s *server) Stop() error {
28     /* impl */
29 }
31 // Wait prevents the server from accepting new connections.
32 func (s *server) Wait() error {
33     /* impl */
34 }

Here is the interface pollution smell list for the code in Listing 1:

  1. The package declares an interface that matches the entire API of its own concrete type.
  2. The factory function returns the interface value with the unexported concrete type value inside.
  3. The interface can be removed and nothing changes for the user of the API.
  4. The interface is not decoupling the API from change.

Let’s break down the code:

On line 04 we see the declaration of the exported interface type Server. This interface declares an exact duplication of the API declared by the unexported concrete type server declared on line 11. These two lines of code check the box for items 1 in the smell list.

Then on line 17 we see the factory function NewServer. This function creates a value of the unexported concrete type server and returns it to the user inside an exported interface value of type Server. This checks the box for item 2 in the smell list.

The next code listing shows how removing the interface changes nothing for the user:

Listing 2:

// Remove the interface and change the concrete type to be exported.

11 type Server struct {
12     /* impl */
13 }

// Have the NewServer function return a pointer of the concrete type instead
// of the interface type.

17 func NewServer(host string) *Server {
18	return &Server{host}
19 }

Having the user work with the concrete type directly doesn’t change anything for the user or the API. This change has actually improved things because the extra level of indirection to call the methods through the interface value has been removed. This checks the box for item 3 in the smell list.

Finally, if we ask what can change in the code, it is never going to be a new implementation of the Server. Having an interface to decouple the server struct type from the API is not helping the API decouple itself from change. This checks the final box for item 4 in the smell list.


Here are some guidelines you can follow to validate and question the use of interfaces in your code:

Use an interface:

  • When users of the API need to provide an implementation detail.
  • When API’s have multiple implementations they need to maintain internally.
  • When parts of the API that can change have been identified and require decoupling.

Go Training

We have taught Go to thousands of developers all around the world since 2014. There is no other company that has been doing it longer and our material has proven to help jump start developers 6 to 12 months ahead of their knowledge of Go. We know what knowledge developers need in order to be productive and efficient when writing software in Go.

Our classes are perfect for both experienced and beginning engineers. We start every class from the beginning and get very detailed about the internals, mechanics, specification, guidelines, best practices and design philosophies. We cover a lot about "if performance matters" with a focus on mechanical sympathy, data oriented design, decoupling and writing production software.

Capital One
Red Ventures

Interested in Ultimate Go Corporate Training and special pricing?

Let’s Talk Corporate Training!

Join Our Online
Education Program

Our courses have been designed from training over 30,000 engineers since 2013, and they go beyond just being a language course. Our goal is to challenge every student to think about what they are doing and why.