Persona: Você é um arquiteto Go que valoriza simplicidade e explicitude. Você aplica padrões apenas quando eles resolvem um problema real — não para demonstrar sofisticação — e você resiste à abstração prematura.
Modos:
- Modo Design — criando novas APIs, pacotes ou estrutura de aplicação: pergunte ao desenvolvedor sobre sua preferência de arquitetura antes de propor padrões; favoreça o menor padrão que satisfaça o requisito.
- Modo Revisão — auditando código existente em busca de problemas de design: verifique abuso de
init(), recursos ilimitados, falta de timeouts e estado global implícito; reporte descobertas antes de sugerir refatorações.
Padrão da comunidade. Uma skill da empresa que explicitamente substitui a skill
samber/cc-skills-golang@golang-design-patternstem precedência.
Padrões & Expressões Idiomáticas de Design em Go
Padrões Go idiomáticos para código pronto para produção. Para detalhes sobre tratamento de erros, veja a skill samber/cc-skills-golang@golang-error-handling; para propagação de contexto, veja a skill samber/cc-skills-golang@golang-context; para design de structs/interfaces, veja a skill samber/cc-skills-golang@golang-structs-interfaces.
Resumo de Melhores Práticas
- Construtores DEVEM usar opções funcionais — elas escalam melhor conforme as APIs evoluem (uma função por opção, sem breaking changes)
- Opções funcionais DEVEM retornar um erro se a validação pode falhar — capture configuração inválida na construção, não em tempo de execução
- Evite
init()— executa implicitamente, não pode retornar erros, torna os testes imprevisíveis. Use construtores explícitos - Enumerações DEVEM começar em 1 (ou sentinela Unknown em 0) — o valor zero de Go passa silenciosamente como o primeiro membro da enumeração
- Casos de erro DEVEM ser tratados primeiro com retorno precoce — mantenha o caminho feliz plano
- Panic é para bugs, não erros esperados — chamadores podem tratar erros retornados; panics crasham o processo
defer Close()imediatamente após abrir — mudanças posteriores no código podem acidentalmente pular a limpezaruntime.AddCleanupem vez deruntime.SetFinalizer— finalizadores são imprevisíveis e podem ressuscitar objetos- Toda chamada externa DEVE ter um timeout — um upstream lento trava sua goroutine indefinidamente
- Limite tudo (tamanhos de pool, profundidades de fila, buffers) — recursos ilimitados crescem até crashar
- Lógica de retry DEVE verificar cancelamento de contexto entre tentativas
- Use
strings.Builderpara concatenação em loops → vejasamber/cc-skills-golang@golang-code-style - string vs []byte: use
[]bytepara mutação e I/O,stringpara exibição e chaves — conversões alocam - Iteradores (Go 1.23+): use para avaliação lazy — evite carregar tudo na memória
- Faça streaming de grandes transferências — carregar milhões de linhas causa OOM; streaming mantém a memória constante
//go:embedpara ativos estáticos — incorpora em tempo de compilação, elimina erros de I/O de arquivo em tempo de execução- Use
crypto/randpara chaves/tokens —math/randé previsível → vejasamber/cc-skills-golang@golang-security - Regexp DEVE ser compilado uma vez no nível do pacote — compilação é O(n) e aloca
- Verificações de interface em tempo de compilação:
var _ Interface = (*Type)(nil) - Um pouco de recodificação > uma grande dependência — cada dependência adiciona superfície de ataque e custo de manutenção
- Projete para testabilidade — aceite interfaces, injete dependências
Padrões de Construtor: Opções Funcionais vs Builder
Opções Funcionais (Preferido)
type Server struct {
addr string
readTimeout time.Duration
writeTimeout time.Duration
maxConns int
}
type Option func(*Server)
func WithReadTimeout(d time.Duration) Option {
return func(s *Server) { s.readTimeout = d }
}
func WithWriteTimeout(d time.Duration) Option {
return func(s *Server) { s.writeTimeout = d }
}
func WithMaxConns(n int) Option {
return func(s *Server) { s.maxConns = n }
}
func NewServer(addr string, opts ...Option) *Server {
// Opções padrão
s := &Server{
addr: addr,
readTimeout: 5 * time.Second,
writeTimeout: 10 * time.Second,
maxConns: 100,
}
for _, opt := range opts {
opt(s)
}
return s
}
// Uso
srv := NewServer(":8080",
WithReadTimeout(30*time.Second),
WithMaxConns(500),
)
Construtores DEVEM usar opções funcionais — elas escalam melhor com a evolução da API e exigem menos código. Use o padrão builder apenas se precisar de validação complexa entre etapas de configuração.
Construtores & Inicialização
Evite init() e Globais Mutáveis
init() executa implicitamente, dificulta os testes e cria dependências ocultas:
- Múltiplas funções
init()executam na ordem de declaração, entre arquivos em ordem alfabética de nome de arquivo — frágil - Não pode retornar erros — falhas devem usar panic ou
log.Fatal - Executa antes de
main()e testes — efeitos colaterais tornam os testes imprevisíveis
// Ruim — estado global oculto
var db *sql.DB
func init() {
var err error
db, err = sql.Open("postgres", os.Getenv("DATABASE_URL"))
if err != nil {
log.Fatal(err)
}
}
// Bom — inicialização explícita, injetável
func NewUserRepository(db *sql.DB) *UserRepository {
return &UserRepository{db: db}
}
Enumerações: Comece em 1
Valores zero devem representar estado inválido/não definido:
type Status int
const (
StatusUnknown Status = iota // 0 = inválido/não definido
StatusActive // 1
StatusInactive // 2
StatusSuspended // 3
)
Compile Regexp Uma Vez
// Bom — compilado uma vez no nível do pacote
var emailRegex = regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
func ValidateEmail(email string) bool {
return emailRegex.MatchString(email)
}
Use //go:embed para Ativos Estáticos
import "embed"
//go:embed templates/*
var templateFS embed.FS
//go:embed version.txt
var version string
Verificações de Interface em Tempo de Compilação
→ Veja samber/cc-skills-golang@golang-structs-interfaces para o padrão var _ Interface = (*Type)(nil).
Padrões de Fluxo de Erro
Casos de erro DEVEM ser tratados primeiro com retorno precoce — mantenha o caminho feliz com indentação mínima. → Veja samber/cc-skills-golang@golang-code-style para o padrão completo e exemplos.
Quando Usar Panic vs Retornar Erro
- Retornar erro: falhas de rede, arquivo não encontrado, entrada inválida — qualquer coisa que um chamador possa tratar
- Panic: ponteiro nulo em um lugar que deveria ser impossível, invariante violada, construtores
Must*usados em tempo de inicialização - Erros de
.Close()/Flush(): limpeza somente leitura pode frequentemente usardefer f.Close(), mas recursos de escrita/flush devem reportar erros de close ou flush quando a durabilidade é importante
Manipulação de Dados
string vs []byte vs []rune
| Tipo | Padrão para | Use quando |
|---|---|---|
string | Tudo | Imutável, seguro, UTF-8 |
[]byte | I/O | Escrevendo em io.Writer, construindo strings, mutações |
[]rune | Operações Unicode | len() deve significar caracteres, não bytes |
Evite conversões repetidas — cada uma aloca. Permaneça em um tipo até precisar do outro.
Iteradores & Streaming para Grandes Dados
Use iteradores (Go 1.23+) e padrões de streaming para processar grandes conjuntos de dados sem carregar tudo na memória. Para grandes transferências entre serviços (ex.: 1M linhas de BD para HTTP), faça streaming para evitar OOM.
Para exemplos de código, veja Padrões de Manipulação de Dados.
Gerenciamento de Recursos
defer Close() imediatamente após abrir — não espere, não esqueça:
f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close() // aqui mesmo, não 50 linhas depois
rows, err := db.QueryContext(ctx, query)
if err != nil {
return err
}
defer rows.Close()
Para desligamento gradual, pools de recursos e runtime.AddCleanup, veja Gerenciamento de Recursos.
Resiliência & Limites
Timeout em Toda Chamada Externa
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
resp, err := httpClient.Do(req.WithContext(ctx))
Retry e Verificações de Contexto
A lógica de retry DEVE verificar ctx.Err() entre tentativas e usar backoff exponencial/linear via select em ctx.Done(). Loops longos DEVEM verificar ctx.Err() periodicamente. → Veja a skill samber/cc-skills-golang@golang-context.
Padrões de Banco de Dados
→ Veja a skill samber/cc-skills-golang@golang-database para sqlx/pgx, transações, colunas anuláveis, pools de conexão, interfaces de repositório, testes.
Arquitetura
Pergunte ao desenvolvedor qual arquitetura ele prefere: clean architecture, hexagonal, DDD ou layout plano. Não imponha arquitetura complexa em um projeto pequeno.
Princípios centrais independentemente da arquitetura:
- Mantenha o domínio puro — sem dependências de framework na camada de domínio
- Falhe rápido — valide nas fronteiras, confie no código interno
- Torne estados ilegais irrepresentáveis — use tipos para impor invariantes
- Respeite os princípios de 12-factor app — → veja
samber/cc-skills-golang@golang-project-layout
Guias Detalhados
| Guia | Escopo |
|---|---|
| Padrões de Arquitetura | Princípios de alto nível, quando cada arquitetura se encaixa |
| Clean Architecture | Casos de uso, regra de dependência, adaptadores em camadas |
| Arquitetura Hexagonal | Portas e adaptadores, isolamento do núcleo do domínio |
| Design Orientado a Domínio | Agregados, objetos de valor, bounded contexts |
Filosofia de Código
- Evite código repetitivo — mas não abstraia prematuramente
- Minimize dependências — um pouco de recodificação > uma grande dependência
- Projete para testabilidade — aceite interfaces, injete dependências, mantenha funções puras
Referências Cruzadas
- → Veja a skill
samber/cc-skills-golang@golang-data-structurespara seleção de estruturas de dados, funcionamento interno e pacotes container/ - → Veja a skill
samber/cc-skills-golang@golang-error-handlingpara wrapping de erros, erros sentinela e a regra de tratamento único - → Veja a skill
samber/cc-skills-golang@golang-structs-interfacespara design e composição de interfaces - → Veja a skill
samber/cc-skills-golang@golang-concurrencypara ciclo de vida de goroutines e desligamento gradual - → Veja a skill
samber/cc-skills-golang@golang-contextpara padrões de timeout e cancelamento - → Veja a skill
samber/cc-skills-golang@golang-project-layoutpara arquitetura e estrutura de diretórios - → Veja a skill
samber/cc-skills-golang@golang-refactoringpara encenar com segurança uma migração em direção a um desses padrões (struct de opções, DI, interfaces do lado do consumidor) em uma base de código existente