Sample Abstract geometric shapes representing Go interfaces

Understanding Go Interfaces

January 15, 2026

Interfaces in Go are one of the language's most powerful features, yet they work differently than in most object-oriented languages. Instead of explicit declarations, Go uses implicit interface satisfaction.

The Basics

An interface in Go is simply a set of method signatures. Any type that implements all those methods automatically satisfies the interface - no implements keyword required.

type Writer interface {
    Write(p []byte) (n int, err error)
}

type ConsoleWriter struct{}

func (cw ConsoleWriter) Write(p []byte) (n int, err error) {
    return fmt.Println(string(p))
}

The ConsoleWriter type now satisfies the Writer interface without any explicit declaration.

Why This Matters

This implicit satisfaction enables a style of programming where you can:

The Empty Interface

The empty interface interface{} (or any in Go 1.18+) is satisfied by every type. Use it sparingly - it bypasses the type system.

"The bigger the interface, the weaker the abstraction." - Rob Pike

Practical Example

Here's how you might use interfaces to make code testable:

// Define interface where it's needed
type UserStore interface {
    GetUser(id string) (*User, error)
}

// Service depends on interface, not concrete type
type AuthService struct {
    users UserStore
}

func (s *AuthService) Authenticate(id, password string) bool {
    user, err := s.users.GetUser(id)
    if err != nil {
        return false
    }
    return user.CheckPassword(password)
}

Now you can easily swap implementations for testing or different storage backends.

Conclusion

Go interfaces encourage composition over inheritance and lead to more flexible, testable code. Start with concrete types, extract interfaces when you need abstraction, and keep them small.