Simplicidade como uma Restrição Deliberada no Design de APIs
A restrição da linguagem Go impulsiona as APIs de bibliotecas e serviços para superfícies pequenas, dependências explícitas e comportamento previsível.
Busque em todas as páginas da documentação
A restrição da linguagem Go impulsiona as APIs de bibliotecas e serviços para superfícies pequenas, dependências explícitas e comportamento previsível.
Isso não é uma preferência de estilo - é como sistemas Go de manutenção sobrevivem a anos de rotatividade de pessoal e atualizações de dependência.
Go favorece APIs "sem graça": poucos construtores, interfaces estreitas, erros explícitos e pacotes que fazem uma única tarefa.
Essa restrição acelera revisões, encolhe matrizes de teste e mantém atualizações compatíveis com a promessa Go 1.
Esta página mostra como projetar limites de pacotes e HTTP que correspondam à linguagem em vez de lutar contra ela.
Quando usar isso: Você está publicando uma biblioteca interna compartilhada, estabilizando um módulo público ou refatorando um "pacote deus" antes de um grande aumento de versão.
Cartão de receita de referência rápida - pronto para copiar e colar.
// Interface do lado do consumidor - um método, definido onde é usado.
type ObjectStore interface {
Put(ctx context.Context, key string, body io.Reader) error
}
// Construtor retorna o tipo concreto; mantenha as opções funcionais.
type S3Store struct { /* ... */ }
func NewS3Store(cfg Config) (*S3Store, error) { /* valida cfg */ return &S3Store{}, nil }
// Função aceita interface, documenta erros.
func UploadReport(ctx context.Context, store ObjectStore, r io.Reader) error {
if err := store.Put(ctx, "report.pdf", r); err != nil {
return fmt.Errorf("upload report: %w", err)
}
return nil
}Quando usar isso:
Config com 20 campos.package checkout
import (
"context"
"errors"
"fmt"
"time"
)
// Erros de domínio são sentinelas de nível de pacote - estáveis para errors.Is.
var ErrInsufficientFunds = errors.New("checkout: insufficient funds")
type PaymentGateway interface {
Charge(ctx context.Context, customerID string, cents int64) (chargeID string, err error)
}
type Service struct {
gw PaymentGateway
now func() time.Time
}
func New(gw PaymentGateway) *Service {
return &Service{gw: gw, now: time.Now}
}
type Request struct {
CustomerID string
Cents int64
}
func (s *Service) Pay(ctx context.Context, req Request) (string, error) {
if req.Cents <= 0 {
return "", fmt.Errorf("checkout: invalid amount %d", req.Cents)
}
id, err := s.gw.Charge(ctx, req.CustomerID, req.Cents)
if err != nil {
return "", fmt.Errorf("checkout: charge: %w", err)
}
return id, nil
}O que isso demonstra:
PaymentGateway) mantém os fakes com uma largura de um método.New documenta as dependências necessárias sem um framework de DI.Request agrupa entradas em vez de aumentar a aridade da função.now mostra como testar o tempo sem exportar globais.implements em boilerplate.%w vincula causas enquanto preserva verificações sentinela via errors.Is e verificações de tipo via errors.As.context.Context é o primeiro parâmetro para cancelamento e valores com escopo de solicitação em limites de I/O.| Regra | Racional | Cheiro de Violação |
|---|---|---|
| Aceitar interfaces, retornar structs | Testabilidade sem mocks amplos | Interface exportada com 10 métodos |
| Uma tarefa por pacote | Grafos de importação claros | Pacote util com ajudantes não relacionados |
| Opções funcionais para botões raros | Evitar Config de 12 campos | New(a,b,c,d,e,f...) |
| Erros estáveis como sentinelas | Roteamento de alertas SRE | Correspondência de strings em logs |
Passar context em I/O | Cancelamento uniforme | context.Background() escondido dentro da biblioteca |
| Camada | Mantenha simples | Adie a complexidade |
|---|---|---|
| Handler | Decodificar, autorizar, chamar serviço, mapear erros para status | Regras de negócio |
| Serviço | Orquestrar etapas de domínio, transações | Detalhes do SQL |
| Repositório | CRUD com contexto | Política |
Para gRPC, prefira mensagens protobuf que espelhem substantivos de domínio, não todos os DTOs internos.
Mapeie erros de domínio para codes.InvalidArgument, codes.NotFound e codes.Internal em um único local.
// Opção funcional - escala sem explosão de Config.
type Option func(*Server)
func WithTimeout(d time.Duration) Option {
return func(s *Server) { s.timeout = d }
}
func NewServer(opts ...Option) *Server {
s := &Server{timeout: 30 * time.Second}
for _, opt := range opts {
opt(s)
}
return s
}Start - mutar servidores em execução é um "pé-de-cabra" comum.Config gigantes - os chamadores não conhecem os campos necessários; valores zero configuram silenciosamente de forma incorreta. Correção: validar em New, retornar erro, fornecer padrões apenas para botões seguros.*sql.DB por todas as camadas - acopla o domínio ao armazenamento. Correção: interface de repositório com tipos de domínio na borda do serviço.internal/platform/core sem chamadores. Correção: esperar pelo segundo consumidor antes de extrair.context.Context por método, nunca salvá-lo em campos./v2) para mudanças de API incompatíveis. Correção: planejar v2 ao remover ou renomear exportações.| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
| Opções funcionais | Vários parâmetros de ajuste opcionais | Apenas uma ou duas dependências necessárias - use New(dep) |
| Padrão Builder (encadeado) | DSL fluente para fixtures de teste complexas | Construtores de produção - opções são mais idiomáticas |
| Frameworks pesados de DI (wire, dig) | Grafos muito grandes com geração de código | Serviços pequenos - wiring manual em main é mais claro |
APIs genéricas (func Do[T any]) | Duplicação real entre tipos | Funções de tipo único - genéricos adicionam ruído |
Singletons globais (var DB) | Protótipos rápidos | Bibliotecas e serviços revisados para produção |
Produtores não devem depender da abstração de cada consumidor.
Pequenas interfaces de consumidor documentam exatamente o que o chamador precisa e mantêm os mocks mínimos.
Sem limite fixo - vise uma responsabilidade coerente.
Se o godoc ler como um catálogo de utilitários não relacionados, divida os pacotes.
Quando a maioria dos chamadores aceita os padrões e uma minoria ajusta timeouts, URLs ou credenciais.
Dependências necessárias pertencem aos parâmetros de New, não às opções.
Use sufixos de versão principal de módulo (/v2) e mantenha os caminhos de importação v1 estáveis de acordo com a promessa de compatibilidade Go 1 para sua própria linha v1.
Retorne erros encapsulados com sentinelas documentados para classificação.
Exporte tipos de erro apenas quando os chamadores precisarem de campos estruturados além de errors.As.
Finns: analisar entrada, chamar um método de serviço, traduzir erros para códigos de status.
Se os manipuladores excederem ~40 linhas, mova a ramificação para serviços testáveis.
Não.
Use genéricos quando eles removerem duplicação real; evite-os quando uma única função for mais clara.
Compartilhe contratos protobuf/OpenAPI, não structs de configuração Go compartilhadas gigantes.
Mantenha o ajuste específico do serviço local; compartilhe apenas substantivos cruzados de serviço estáveis.
Pacotes expressam limites; arquivos organizam a legibilidade dentro de um limite.
Divida pacotes quando ciclos de importação ou conceitos não relacionados aparecerem, não quando arquivos parecerem longos sozinhos.
Use comentários godoc em símbolos exportados, entradas CHANGELOG e tags de módulo.
Chame APIs experimentais de Experimental em nome ou documente a instabilidade no comentário do pacote.
Exporte campos quando os invariantes forem simples.
Use métodos quando validação, bloqueio ou valores derivados forem necessários - não simetria JavaBean.
APIs simples podem esconder pooling ou batching dentro do pacote.
Perfira antes de adicionar complexidade às superfícies exportadas - mantenha as otimizações não exportadas.
Versões da Stack: Esta página foi escrita para Go 1.26.x (padrão Green Tea GC, go fix modernizers - verifique o patch na compilação), chi (última versão - verifique na compilação), gin (última versão - verifique na compilação), echo (última versão - verifique na compilação), google.golang.org/grpc (última versão - verifique na compilação), sigs.k8s.io/controller-runtime (última versão - verifique na compilação), kubebuilder (última versão - verifique na compilação), tinygo (última versão - verifique os alvos de placa na compilação), wazero (última versão - verifique na compilação) e golangci-lint (última versão - verifique o conjunto de linters na compilação).
Revisado por Chris St. John·Última atualização: 19 de jul. de 2026