When to Use Interfaces vs Concrete Types in Go

Use interfaces for flexibility and decoupling, and concrete types for performance and fixed implementations.

Use interfaces when you need polymorphism, dependency injection, or to decouple code from specific implementations; use concrete types when performance matters or the implementation is fixed. Interfaces add a small runtime cost for dynamic dispatch, while concrete types allow the compiler to inline methods and optimize aggressively.

// Use an interface for flexibility
func Process(r io.Reader) error {
    // Works with bytes.Reader, os.File, net.Conn, etc.
    _, err := io.ReadAll(r)
    return err
}

// Use a concrete type for performance
func FastProcess(b []byte) error {
    // Compiler inlines directly; no interface overhead
    _ = len(b)
    return nil
}