Advantages of interfaces in GO

In GO, interfaces are different from other languages. They are slightly better than in other popular languages ​​in terms of design. In this article I will try to explain why.
I’ll talk about the benefits, give examples and discuss some issues that may arise when using interfaces.

What is the specificity of interfaces in GO?

In most languages, you describe one interface, and implement them in other places explicitly, indicating that you implement them. But in GO this is not so. You do not need to indicate explicitly that you are implementing it in your structure. If the structure has all the functions and they have the same signature with the interface, then it already implements it.

What is the advantage of this feature?

Private interface

Interfaces are conveniently described inside the module. I will explain why this is good. Suppose you are writing a package. In a good way, it should be minimally dependent on other packages, and for this you are fenced off from them by the interface. This will allow you to test your package in isolation and, if necessary, replace the external library. But this interface can be private, i.e. other packages know nothing about it.

At a particular moment, specific methods from some external object are needed, and for this it is enough to have an interface only with these specific methods. You do not need to look for suitable interfaces for the created module from external or general packages.

Let's look at an example.

I am writing a package responsible for authorizing a user. It is necessary to access the user repository. We need only a few methods, we will describe them in the interface:

package auth import ( "" ) type userRepository interface { FindUserByEmail(email string) (backend.User, error) AddUser(backend.User) (userID int, err error) AddToken(userID int, token string) error TokenExists(userID int, token string) bool }//Auth сервис авторизации type Auth struct { repository userRepository logger backend.Logger }//NewAuth создает объект авторизации func NewAuth(repository userRepository, logger backend.Logger) *Auth { return &Auth{repository, logger} }//Autentificate Проверяет существование токена пользователя func (auth Auth) Autentificate(userID int, token string) bool { return auth.repository.TokenExists(userID, token) } 

For an example, I showed how one of the methods is used, in fact, they are all used.

In the main main method, an authorization object is created and used:

package main import ( "" "" ) func main() { logger := newLogger() userRepository := mysql.NewUserRepository(logger) err := userRepository.Connect() authService := auth.NewAuth(userRepository, logger)... 

When creating the authorization object, it is enough to pass userRepository, which implements all the methods that are in the interface, and the mysql package does not know anything about the interface described in the authorization service. He should not know about it. There are no unnecessary dependencies. The code remains clean.

In other programming languages, you would have to describe the interface in a common module. And indicate in the repository class that it implements this interface. And in the authorization class use it. Although, in fact, only the authorization module needs to know about this interface, because only he needs it.

If you pass an object that does not implement the desired interface, you will receive an error at the compilation stage.

This interface is convenient to extend

If you need other methods from the repository in the future, you can simply add them to this private interface. It gets a little complicated, but only inside this module. You do not need to dock them in some common interface, and then describe the methods wherever it is implemented.


In tests, it is also convenient to use this interface. It is enough to replace the methods that will be used in the module. No need to replace anything superfluous.

Example moka:

type userRepositoryMock struct { user backend.User findUserErr error addUserError error addUserID int addTokenErr error tokenExists bool } func (repository userRepositoryMock) FindUserByEmail(email string) (backend.User, error) { return repository.user, repository.findUserErr } func (repository userRepositoryMock) AddUser(backend.User) (userID int, err error) { return repository.addUserID, repository.addUserError } func (repository userRepositoryMock) AddToken(userID int, token string) error { return repository.addTokenErr } func (repository userRepositoryMock) TokenExists(userID int, token string) bool { return repository.tokenExists } 

Further, in tests, userRepositoryMock can be slipped instead of the usual userRepositor, substituting the necessary values ​​that the function should return.

How to understand who actually implements the method used from the interface?

It seems that by closing the interface and not explicitly indicating who implements it, we lose our knowledge of how the desired function actually works. But, since GO is a strongly typed language, finding out who implements the method is quite simple. For example, the GoLand IDE can do this.

To make sure that the structure implements the interface, just run the compiler. He will show which methods are missing.

How to find places where implemented methods are used if they are closed by the interface?

The answer is the same. This is also easily amenable to static analysis. If your IDE cannot find an implementation, then this is its flaw, not the interfaces. If the IDE is developing, then in the near future this function should appear.


Interfaces in GO make code a little easier. Hiding unnecessary dependencies, simplicity in code extension and easy testing are the advantages that interfaces in this language have. I do not claim that this is a great advantage. But if you are developing a large project, this is important.

The GO language seems like a suitable tool for developing large projects. And interfaces are one of those features that has been done successfully.