Interface Design Principles
Intermediate
Design Go interfaces the idiomatic way — accept interfaces, return structs, keep interfaces small, define them at the consumer side, and never export interfaces with only one implementation.
File Patterns
**/*.go**/go.mod
This rule applies to files matching the patterns above.
Rule Content
rule-content.md
# Interface Design Principles
## Rule
Define interfaces at the consumer side, not the implementation side. Keep interfaces small (1-3 methods). Accept interfaces, return concrete types. Never create interfaces preemptively.
## Good Examples
```go
// Consumer defines the interface it needs
package order
// Only the methods order package actually calls
type PaymentProcessor interface {
Charge(ctx context.Context, amount int64, currency string) (string, error)
}
type Service struct {
payments PaymentProcessor // Depends on interface
}
func NewService(p PaymentProcessor) *Service {
return &Service{payments: p} // Returns concrete type
}
```
```go
// Small, focused interfaces
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
// Compose when needed
type ReadWriter interface {
Reader
Writer
}
```
## Bad Examples
```go
// BAD: Interface defined at implementation side
package payment
type Processor interface { // Don't export from implementation package
Charge(ctx context.Context, amount int64, currency string) (string, error)
Refund(ctx context.Context, chargeID string) error
GetBalance(ctx context.Context) (int64, error)
ListTransactions(ctx context.Context) ([]Transaction, error)
}
// BAD: Fat interface — too many methods
type Repository interface {
Create(ctx context.Context, u *User) error
Update(ctx context.Context, u *User) error
Delete(ctx context.Context, id string) error
GetByID(ctx context.Context, id string) (*User, error)
GetByEmail(ctx context.Context, email string) (*User, error)
List(ctx context.Context, filter Filter) ([]*User, error)
Count(ctx context.Context) (int64, error)
}
// Split into smaller interfaces consumed by different packages
// BAD: Returning interface instead of concrete type
func NewService() ServiceInterface { ... } // Return *Service instead
```
## The Go Proverb
> "The bigger the interface, the weaker the abstraction." — Rob Pike
## Enforcement
- Code review: verify interfaces are defined at consumer, not provider
- Lint: flag exported interfaces with only one implementation
- Prefer 1-3 method interfaces — split larger onesFAQ
Discussion
Loading comments...