Clean Code em Go: princípios para escrever código legível e sustentável
Código limpo não é um luxo, mas uma necessidade para qualquer projeto sério. Na linguagem Go, conhecida por sua simplicidade e concisão, os princípios de Clean Code são especialmente importantes. Código mal escrito em Go rapidamente se torna ilegível e difícil de manter, apesar de todas as vantagens da linguagem. Neste artigo, vamos abordar os principais princípios para escrever código limpo em Go com exemplos concretos.
1. Nomenclatura correta: a base da legibilidade
Em Go, vale a regra: quanto menor o escopo, mais curto o nome. No entanto, isso não significa que você deve usar variáveis de uma letra em todo lugar. Vejamos exemplos.
Ruim (código ilegível):
func f(x int, y int) int { z := x + y return z}Bom (código limpo):
func Sum(a int, b int) int { result := a + b return result}Regras importantes de nomenclatura em Go:
- Variáveis: use camelCase (por exemplo,
userName,totalCount) - Funções e tipos exportados (públicos): com letra maiúscula —
func CalculateTax() - Não exportados (privados): com letra minúscula —
func parseInput() - Abreviações: escreva em letras maiúsculas (por exemplo,
HTTPHandler, nãoHttpHandler) - Evite notação húngara: não escreva
strNameouintCount— o tipo já é óbvio
2. Tratamento de erros: explícito e conciso
Go não usa exceções — erros são retornados como valores. Código limpo exige tratamento explícito de cada erro. Não os ignore!
Ruim (ignorando o erro):
file, _ := os.Open("data.txt")// Erro perdido!Bom (tratamento explícito):
file, err := os.Open("data.txt")if err != nil { log.Fatalf("Não foi possível abrir o arquivo: %v", err)}defer file.Close()Dicas adicionais:
- Sempre verifique
err != nilimediatamente após chamar uma função - Use
deferpara fechar recursos - Crie seus próprios tipos de erro com
fmt.Errorf("contexto: %w", err) - Não use pânico (
panic) para erros comuns — isso é para situações excepcionais
3. Funções: curtas e com uma única responsabilidade
O Princípio da Responsabilidade Única (Single Responsibility Principle) também se aplica a funções. Cada função deve fazer apenas uma coisa.
Ruim (função faz demais):
func ProcessUserData(userID int) (string, error) { // 1. Conexão com o banco db, _ := sql.Open("postgres", "...") // 2. Consulta de dados row := db.QueryRow("SELECT name, email FROM users WHERE id = ?", userID) // 3. Formatação var name, email string row.Scan(&name, &email) result := fmt.Sprintf("Nome: %s, Email: %s", name, email) // 4. Envio de email (efeito colateral!) sendEmail(email, "Saudação") return result, nil}Bom (divisão em funções pequenas):
func GetUserName(db *sql.DB, userID int) (string, error) { var name string err := db.QueryRow("SELECT name FROM users WHERE id = ?", userID).Scan(&name) return name, err}
func FormatUserInfo(name, email string) string { return fmt.Sprintf("Nome: %s, Email: %s", name, email)}
func ProcessUserData(db *sql.DB, userID int) (string, error) { name, err := GetUserName(db, userID) if err != nil { return "", fmt.Errorf("erro ao obter usuário: %w", err) } email, _ := getUserEmail(db, userID) /